JavaScript 内建的继承方法被称为 原型对象链(又叫原型对象继承)。app
原型对象的属性可经由对象实例访问,这就是继承的一种形式。ide
对象实例继承了原型对象的属性,而原型对象也是一个对象,它也有本身的原型对象并继承其属性,以此类推。这就是原型对象链。函数
全部对象(包括自义定的)都自动继承自 Object
,除非你另有指定。更确切地说,全部对象都继承自 Object.prototype
。任何以对象字面量形式定义的对象,其 [[Prototype]]
的值都被设为 Object.prototype
,这意味着它继承 Object.prototype
的属性。ui
Object.prototype 通常有如下几个方法:this
这 5 种方法经由继承出如今全部对象中。 由于全部对象都默认继承自 Object.prototype
,因此改变它就会影响全部的对象。因此不建议。spa
对象继承是最简单的继承类型。你惟须要作的是指定哪一个对象是新对象的 [[Prototype]]
。对象字面量形式会隐式指定 Object.prototype
为其 [[Protoype]]
。固然咱们能够用 ES5 的 Object.create()
方法显式指定。该方法接受两个参数,第一个是新对象的 [[Prototype]]
所指向的对象。第二个参数是可选的一个属性描述对象,其格式与 Object.definePrototies()
同样。prototype
var obj = {
name: "Ljc"
};
// 等同于
var obj = Object.create(Object.prototype, {
name: {
value: "Ljc",
configurable: true,
enumberable: true,
writable: true
}
});
复制代码
下面是继承其它对象:code
var person = {
name: "Jack",
sayName: function(){
console.log(this.name);
}
}
var student = Object.create(person, {
name:{
value: "Ljc"
},
grade: {
value: "fourth year of university",
enumerable: true,
configurable: true,
writable: true
}
});
person.sayName(); // "Jack"
student.sayName(); // "Ljc"
console.log(person.hasOwnProperty("sayName")); // true
console.log(person.isPrototypeOf(student)); // true
console.log(student.hasOwnProperty("sayName")); // false
console.log("sayName" in student); // true
复制代码
当访问一个对象属性时,JavaScript 引擎会执行一个搜索过程。若是在对象实例存在该自有属性,则返回,不然,根据其私有属性 [[Protoype]]
所指向的原型对象进行搜索,找到返回,不然继承上述操做,知道继承链末端。末端一般是 Object.prototype
,其 [[Prototype]]
是 null
。对象
固然,也能够用 Object.create()
常见一个 [[Prototype]]
为 null
的对象。继承
var obj = Object.create(null);
console.log("toString" in obj); // false
复制代码
该对象是一个没有原型对象链的对象,便是一个没有预约义属性的白板。
JavaScript 中的对象继承也是构造函数继承的基础。
第四章提到,几乎全部函数都有 prototype
属性,它可被修改或替换。该 prototype
属性被自动设置为一个新的继承自 Object.prototype
的泛用对象,该对象(原型对象)有一个自有属性 constructor
。
实际上,JavaScript 引擎为你作了下面的事情。
// 你写成这样
function YourConstructor(){
// initialization
}
// JavaScript引擎在背后为你作了这些处理
YourConstructor.prototype = Object.create(Object.prototype, {
constructor: {
configurable: true,
enumerable: true,
value: YourConstructor,
writable: true
}
})
复制代码
你不须要作额外的工做,这段代码帮你把构造函数的 prototype
属性设置为一个继承自 Object.prototype
的对象。这意味着 YourConstructor
建立出来的任何对象都继承自 Object.prototype
。
因为 prototype 可写,你能够经过改变它来改变原型对象链。
instanceof 运算符能够用来判断某个构造函数的 prototype 属性是否存在另一个要检测对象的原型链上。
function Rectangle(length, width){
this.length = length;
this.width = width
}
Rectangle.prototype.getArea = function(){
return this.length * this.width
}
Rectangle.prototype.toString = function(){
return "[Rectangle " + this.length + "x" + this.width + "]";
}
// inherits from Rectangle
function Square(size){
this.length = size;
this.width = size;
}
Square.prototype = new Rectangle(); // 尽管是 Square.prototype是指向了 Rectangle的对象实例,即Square的实例对象也能访问该实例的属性(若是你提早声明了该对象,且给该对象新增属性)。
// Square.prototype = Rectangle.prototype; // 这种实现没有上面这种好,由于Square.prototype 指向了 Rectangle.prototype,致使修改Square.prototype时,实际就是修改Rectangle.prototype。
console.log(Square.prototype.constructor); // 输出 Rectangle 构造函数
Square.prototype.constructor = Square; // 重置回 Square 构造函数
console.log(Square.prototype.constructor); // 输出 Square 构造函数
Square.prototype.toString = function(){
return "[Square " + this.length + "x" + this.width + "]";
}
var rect = new Rectangle(5, 10);
var square = new Square(6);
console.log(rect.getArea()); // 50
console.log(square.getArea()); // 36
console.log(rect.toString()); // "[Rectangle 5 * 10]", 但若是是Square.prototype = Rectangle.prototype,则这里会"[Square 5 * 10]"
console.log(square.toString()); // "[Square 6 * 6]"
console.log(square instanceof Square); // true
console.log(square instanceof Rectangle); // true
console.log(square instanceof Object); // true
复制代码
Square.prototype
并不真的须要被改为为一个 Rectangle
对象。事实上,是 Square.prototype
须要指向 Rectangle.prototype
使得继承得以实现。这意味着能够用 Object.create()
简化例子。
// inherits from Rectangle
function Square(size){
this.length = size;
this.width = size;
}
Square.prototype= Object.create(Rectangle.prototype, {
constructor: {
configurable: true,
enumerable: true,
value: Square,
writable: true
}
})
复制代码
在对原型对象添加属性前要确保你已经改为了原型对象,不然在改写时会丢失以前添加的方法(由于继承是将被继承对象赋值给须要继承的原型对象,至关于重写了须要继承的原型对象)。
因为 JavaScript 中的继承是经过原型对象链来实现的,所以不须要调用对象的父类的构造函数。若是确实须要在子类构造函数中调用父类构造函数,那就能够在子类的构造函数中利用 call
、apply
方法调用父类的构造函数。
// 在上面的代码基础上做出修改
// inherits from Rectangle
function Square(size){
Rectangle.call(this, size, size);
// optional: add new properties or override existing ones here
}
复制代码
通常来讲,须要修改 prototype
来继承方法并用构造函数窃取来设置属性,因为这种作法模仿了那些基于类的语言的类继承,因此这一般被称为伪类继承。
其实也是经过指定 call
或 apply
的子对象调用父类方法。