一、继承之借用构造函数
借用构造函数:通过在子类型构造函数使用 call() 或 apply() 方法继承超类构造函数的属性和方法。
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'gree'];
}
function SubType(name, age) {
SuperType.call(this, name); // 借用构造函数
this.age = age;
}
var instance1 = new SubType('Nicholas', 29); /* ① */
instance1.colors.push('black'); /* ② */
console.log(instance1.colors); /* ③ */
执行 var instance1 = new SubType('Nicholas', 29); 时,实际会执行如下步骤:
1)创建一个新对象
2)把构造函数(SubType)的作用域赋值给新创建的对象
3)执行构造函数(SubType)中的代码,其内部执行过程为:
i)当执行完 SuperType.call(this, name) 时,SubType 构造函数拥有了 SuperType 构造函数中的属性,
即新创建的对象拥有 name = 'Nicholas'、colors = ['red', 'blue', 'gree'] 属性
ii)age = 29
4)返回新创建的对象(并赋值给 instance1)
所以 instance1 对象中拥有的实例属性为:name、colors、age,其值分别为 'Nicholas'、 ['red', 'blue', 'gree'] 、29。
二、继承之原型链
原型链:子构造函数的原型对象为超类型构造函数的实例,即 SubType.prototype = new SuperType()。
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'gree'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
this.name = name;
this.age = age;
}
// 原型链继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}
var instance1 = new SubType('Nicholas', 29);
console.log(instance1.name);
instance1.sayName();
instance1.colors.push('black');
console.log(instance1.colors); //['red', 'blue', 'gree', 'black']
var instance2 = new SubType('Greg', 30);
console.log(instance2.colors); // ['red', 'blue', 'gree', 'black']
上述代码的原型链图如下所示
由上图可知:
1)当调用 instance1.name,其执行的是 instance1 对象实例中的属性,即为 'Nicholas'。该属性屏蔽了原型链中的 name 属性。
2)当调用 instance1.sayName(),实际上是继承了 SuperType.prototype 对象的方法,由于 this 指向的是当前对象 instance1,即语句执行的结果为显示 'Nicholas'
而由于通过原型继承的方法和属性是共享的,如果原型链中包含引用类型的属性时,当有一个对象改变该属性时,创建的其他对象都会共享该属性改变后的值。
3)当执行 instance1.colors.push('black') 后,原型链中 colors 属性的值为 ['red', 'blue', 'gree', 'black']
此后再创建一个新对象 instance2,instance2.colors 的值为即为 ['red', 'blue', 'gree', 'black']
三、继承之组合继承(借用构造函数和原型链)
组合继承就是结合借用构造函数和原型链的优点来实现继承,具体代码如下所示:
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'gree'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name); // 借用构造函数
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}
var instance1 = new SubType('Nicholas', 29);
console.log(instance1.name);
instance1.colors.push('black');
console.log(instance1.colors); // ['red', 'blue', 'gree', 'black']
var instance2 = new SubType('Greg', 30);
console.log(instance2.colors); // ['red', 'blue', 'gree']
当执行 var instance1 = new SubType('Nicholas', 29) 时,其原型链图如下图所示:
由上图可知,instance1 对象中拥有 colors 属性,该属性屏蔽了原型链中的属性,属于实例属性。
即修改 instance1 的 colors 属性不会被其他实例所共享。
参考文献
[1] 《JavaScript高级程序设计(第3版)》