Appearance
原型链继承
本节目标:理解“原型链继承”的核心思路,并能把它用于实际开发或面试表达。 原型链继承是
ES5实现继承的主要方法。
**基本思想:**将子类构造函数的prototype指向父类的实例。
SubType.prototype = new SuperType();原理:
- 父类实例可共享父类原型上的属性和方法。
- 子类实例可共享子类原型上的属性和方法。
- 把父类实例设为子类的原型后,子类实例就可以共享父类实例上的属性和方法,同时还能共享父类原型的属性和方法。
构造函数、原型和实例三者关系:
- 构造函数都有原型属性,指向原型对象
;当把函数当成构造函数的时候用到。 - 原型对象的
constructor属性它默认指向构造函数; - 实例共享原型对象上的属性和方法;
代码实现 实现原型链有一种基本模式,其代码大致如下。
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
}
function SubType() {
this.subproperty = false;
}
//继承了
SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue());//true示例2 function Parent() { this.name = 'Parent'; } Parent.prototype.sayHello = function() { console.log('Hello from Parent'); }; function Child() { this.age = 18; } // 设置 Child 的原型为 Parent 的实例 Child.prototype = new Parent(); const child = new Child(); console.log(child.name); // 输出: Parent child.sayHello(); // 输出: Hello from Parent
在上面的代码中,我们没有使用SubType默认提供的原型,而是给它换了一个新的原型;这个新原型就是SuperType的实例。于是,新原型不仅具有作为一个SuperType的实例所拥有的全部属性和方法,而且其内部还有一个指针,指向了SuperType的原型。要注意的是,instance.constructor现在指向的是SuperType,因为其原型被重写了。实际上,不是SubType的原型的constructor属性被重写了,而是SubType的原型指向了另一个对象:SuperType的原型,而这个原型对象的construcotr指向SuperType.
原型链本质上扩展了原型搜索机制。就拿上面的例子来说,调用instance.getSuperValue()会经历三个搜索步骤:
- 搜索实例;
- 搜索
SubType.prototype(也就是父类实例); - 搜索
SuperType.prototype;
在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。
别忘记默认的原型 所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。所有函数的默认原型都是Object实例,因此默认原型都会包含一个内部指针,指向Object.prototype。这也正是所有自定义类型都会继承toString(), valueOf()等默认方法的根本原因。
确定原型和实例的关系 有两种方式: 第一种方式是使用instanceof操作符; 第二种是使用isPrototypeOf(另有Object.getPrototypeOf)方法。同样,只要是原型链接中出现过的原型,都可以说是该原型链所派生的实例的原型。
谨慎地定义方法 子类有时候需要重写超类中的某个方法,或者需要添加超类型中不存在的某个方法。但给原型添加方法的代码一定要放在替换原型的语句之后。 此外,在通过原型链实现继承时,不能使用对象字面量创建原型方法。因为这样会重写原型链。
原型链的问题 第一、修改子类实例属性会影响其它子类实例:由于父类实例属性为所有子类共享:
- 如果父类实例的属性是基本类型的,子类实例修改同名属性会覆盖原型同名属性,倒也无关紧要;
- 如果父类实例的属性是引用类型的,子类实例对此属性的修改会影响到其它子类实例。
- 一般父类实例的属性在父类构造函数中用
this来指定。
function SuperType() {
this.colors = ['red', 'blue', 'green'];
}
function SubType() {
}
//继承了
SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push('black');
var instance2 = new SubType();
console.log(instance2.colors);//'red,blue,green,black'第二、在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。所在,实践中,很少会单独使用原型链。
