《JS高级程序设计》第6章的读书笔记
- 创建对象(一)工场模式和构造函数模式
- 创建对象(二)原型模式和组合模式
- 创建对象(三)再探原型
- 对象继承(一)原型链
- 对象继承 (二)借用构造函数和组合继承
- 对象继承(三)原型式继承和寄生式继承
- 对象继承(四)寄生组合式继承
1 前言
我前后写了3篇博文阐述创建对象的4种方式。
更为可怕的是:《JS高级程序设计》阐述6种对象的方式,虽然其中只有一种方式是常用的。学习了这部分内容,我觉得,如此繁杂的继承方式,即使当初创造JS的作者也会觉得吃惊。
2 原型链(继承)
原型链自然是基于原型的。我在之前的博客:JS高级程序设计》第6章读书笔记:创建对象(二)原型模式和组合模式以及《JS高级程序设计》第6章读书笔记:创建对象(三)再探原型 两篇文章阐述了原型的概念和细节。
还是直接看实现原型链的代码吧
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType(); // 这里是关键
SubType.prototype.getSubValue = function() {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue()); // true
我们可以看到子类型的实例instance
能够调用父类型的方法getSuperValue
,所以我们可以说子类型继承了父类型。
继承后的结果如下图:
而实现继承的关键在于这行代码
SubType.prototype = new SuperType(); // 这里是关键
用自然语言表述的话:重写子类的原型对象为父类的实例对象。
注意一个异常:继承之后,instance.constructor现在指向SuperType,这是因为子类型原型被重写后,子类原型没有constructor这个属性了。
造成的结果:原型搜索结果被扩展,访问属性时,会先后搜索实例对象,子类原型对象,父类原型对象,Object的原型
确定原型和实例的关系
第一种方式:instanceof操作符
基于最上面的基础代码
console.log(instance instanceof Object); //true
console.log(instance instanceof SuperType); //true
console.log(instance instanceof SubType); //true
第二种方式:isPropertyOf方法
基于最上面的基础代码
console.log(Object.prototype.isPrototypeOf(instance)); //true
console.log(SuperType.prototype.isPrototypeOf(instance)); //true
console.log(SubType.prototype.isPrototypeOf(instance)); //true
谨慎地定义方法
在需要覆盖或者添加超类型中不存在的某个方法时,一定要在替换原型的语句之后
SubType.prototype.getSubValue = function(){ //添加超类型之后不存在的某个方法
return this.subproperty;
}
SubType.prototype.getSuperValue = functipn(){ //覆盖超类型方法
return false;
}
注意:通过原型链实现继承时,不能使用对象字面量方法创建原型方法
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype = { //
getSubValue: function() {
return this.subproperty;
},
someOtherMethod: function() {
return false;
}
}
SubType.prototype.getSubValue = function() {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue()); // instance.getSuperValue is not a function
很简单:重写原型后,SubType.prototype就没有[[prototype]]属性指向父类型的原型对象
原型链的问题
第1个问题
原型链的问题和原型的问题紧密相关。我们知道,原型问题是包含引用类型值的原型属性会被所有实例共享。所以,我们要在构造函数中,而不是在原型对象中定义属性。因而,构造函数模式和原型模式相结合的组合模式解决了这个问题,成为最常用的创建对象的模式。
但是在用原型链实现集成继承时,原先的实例属性成了子类型的原型属性。(因为子类型的原型是父类型的实例。)
具体看下面这段代码:
function SuperType() {
this.colors = ['red', 'blue', 'green'];
}
function SubType() {}
SubType.prototype = new SuperType();
let instance1 = new SubType();
instance1.colors.push('black');
console.log(instance1.colors); //[ 'red', 'blue', 'green', 'black' ]
let instance2 = new SubType();
console.log(instance2.colors); //[ 'red', 'blue', 'green', 'black' ]
第2个问题
无法再不影响所有对象实例的情况下给超类型传递参数。因为子类型实例共享了子类型原型的属性。
具体看代码:
function SuperType(name) {
this.name = name;
}
function SubType() {}
SubType.prototype = new SuperType('achao');
let instance1 = new SubType();
console.log(instance1.name); //achao
let instance2 = new SubType();
console.log(instance2.name); //achao
上述代码没有彻底实现自定义子类型的意愿。