《JavaScript高级程序设计》第6章补充 继承
基于原型链继承
将父类的实例赋给子类的prototype来实现继承。
原理:父类的实例有父类所有的实例属性和原型方法,将它赋给子类的prototype后,子类的创建的实例就有会__proto__属性指向这个prototype(它拥有父类所有的实例属性和原型方法),实现继承。
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; }
原型链继承的问题:本来在父类中的实例属性(如上图property),到了子类中变成了原型属性,这样违反了每个实例有自己独有的实例属性的原则(如果是个数组的话又会一改就全部实例都改了)。
借调用父类的构造函数继承
在子类的构造函数中调用父类的构造函数,同时用call把this绑定在对应的实例上(即目前的this)。
function SuperType() { // SubType的实例(instance1、instance2)来到这里之后,color变成了他们的属性(实例属性) this.color = ["red", "blue", "green"]; } // 这里的this是SubType的实例(instance1、instance2),然后借调用父类的构造函数,并把this传进去。 function SubType() { SuperType.call(this); } var instance1 = new SubType(); instance1.color.push("black"); console.log(instance1.color); var instance2 = new SubType(); console.log(instance2.color);
借调用构造函数的方式的问题也很明显:所有方法都不能在原型上定义了,本来方法应该要定义在原型中,达到重用的目的。
组合继承
原型链实现对原型属性和方法的继承,借调用构造函数实现对实例属性的继承。
function SuperType(name) { this.name = name; this.color = ["red", "blue", "green"]; } SuperType.prototype.sayName = function () { console.log(this.name); } function SubType(name, age) { // 继承父类属性 SuperType.call(this, name); // 添加子类自己的实例属性 this.age = age; } // 继承父类方法(原型方法) SubType.prototype = new SuperType(); // 虽然这样color还是变成了SubType的原型属性,但是不要紧,借调用的时候实例会被重新添加一次实例属性color // 这个实例属性color就会屏蔽掉原型属性color SubType.prototype.constructor = SubType; // 添加子类自己的原型方法 SubType.prototype.sayAge = function () { console.log(this.age); } var instance1 = new SubType("Nicholas", 29); instance1.color.push("black"); console.log(instance1.color); // ["red", "blue", "green", "black"] var instance2 = new SuperType("Greg", 27); console.log(instance2.color); // ["red", "blue", "green"]
这是最常用的继承模式。
寄生组合式继承
由于子类的prototype并不想要父类的实例属性,我们只想要子类的prototype的__proto__可以指向父类的prototype而已。
所以用 SubType.prototype = new SuperType(); 这种方式实际上不是很完美
// 这个函数的作用简单来说就是输入一个对象,返回一个__proto__指向它的对象 function object(o) { function F() {} F.prototype = o; return new F(); } // 这个函数的作用就是让子类的prototype的__proto__可以指向父类的prototype function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } function SuperType() { // 父类的属性 this.name = name; this.color = ["red", "blue", "green"]; } // 父类的方法 SuperType.prototype.sayName = function () { console.log(this.name); } function SubType(name, age) { // 继承父类属性 SuperType.call(this, name); // 子类自己的属性 this.age = age; } // 继承父类方法(对子类的prototype动手脚),但不是像以前那样简单地 SubType.prototype = new SuperType(); inheritPrototype(SubType, SuperType); // 子类自己的方法 SubType.prototype.sayAge = function () { console.log(this.age); }
这样SubType.prototype上就不会有不必要的属性了。