- 原型链(父类的实例)
- 借用构造函数(调用超类构造函数)
- 组合继承(原型链和借用构造函数)
- 原型式继承(借助原型基于已有的对象上创建新对象)
- 寄生式继承(创建一个仅用于封装继承过程的函数)
- 寄生组合式继承(解决父类属性重写)
ECMAScript无法实现接口继承,只支持实现继承,而且其实现继承主要依靠原型链来实现的
原型链
思想:利用原型让一个引用类型继承另一个引用类型的属性和方法。
1 function A() { 2 this.name = 'A'; 3 } 4 A.prototype = { 5 sayName: function() { 6 return this.name; 7 } 8 }; 9 function B() { 10 this.name = 'B'; 11 } 12 //通过原型链继承对象A 13 B.prototype = new A(); 14 var b = new B(); 15 debug(b.sayName()); //'B'
1、 别忘记默认的原型(所有引用类型默认都继承Object,且通过原型链继承)
2、 确定原型和实例的关系
instanceof操作符和isPrototype()方法。
3、 谨慎地定义方法
给子类添加原型的方法的代码一定要放在替换原型的语句之后。
通过原型链实现的继承时,不能使用对象字面量创建原型方法。这样会重写原型方法。
4、 原型链问题
1、 创建子类时不能向超类的构造函数传递参数
2、 超类包含引用类型的属性,通过原型来实现继承时,实际上会变成另一个类型的实例,于是乎超类的实例的所有属性变成了子类的原型属性了。因而引发了共享属性问题(特别是引用类型属性)
借用构造函数(经典继承)
思想:在子类型构造函数的内部调用超类型构造函数。
1 function A() { 2 this.colors = ['red', 'blue', 'green']; 3 } 4 //借用构造函数实现继承 5 function B() { 6 A.call(this); //把A在B的环境中执行,A与B中的this对象指向B的实例 7 } 8 var b = new B(); 9 b.colors.push('black'); 10 debug(b.colors); //['red', 'blue', 'green', 'black'] 11 var b2 = new B(); 12 debug(b2.colors); //['red', 'blue', 'green'] 13 1、 向超类中传递参数 14 function A(name) { 15 this.name = name; 16 } 17 //借用构造函数实现继承 18 function B(name, age) { 19 //把A在B的环境中执行,A与B中的this对象指向B的实例 20 A.call(this, name); 21 this.age = age; 22 } 23 var b = new B('mackxu', 22); 24 debug('我的名字是:'+b.name+" 我的年龄:"+b.age);
2、 借用构造函数的问题
1) 与构造函数模式一样的问题:在构造函数中定义的方法,无法共享。
2) 更苦逼的事:在超类的原型中定义的方法,对子类不可见
组合继承:(伪经典继承)
思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
结果:既通过在原型上定义方法实现了函数的复用,又能够保证每个实例都有它自己的属性。
问题:无论什么情况下,都会调用两次超类型构造函数。一次在创建子类型原型时候、另一次在子类型构造函数内部。造成不得不在子类型构造函数中重写父类的属性。
解决办法:寄生组合式继承[改写了原型链继承]
1 function A(name) { 2 this.name = name; 3 this.colors = ['red', 'blue', 'green']; 4 } 5 A.prototype = { 6 sayName: function() { 7 return this.name; 8 } 9 } 10 //借用构造函数实现继承超类属性 11 function B(name, age) { 12 //把A在B的环境中执行,A与B中的this对象指向B的实例 13 A.call(this, name); 14 this.age = age; 15 } 16 //原型链继承超类的属性(方法和属性) 17 B.prototype = new A(); 18 //此处不能用对象字面量赋值(原因前面已经给出) 19 B.prototype.sayAge = function() { 20 return this.age; 21 } 22 var b = new B('mackxu', 22); 23 debug('我的名字是:'+b.sayName()+" 我的年龄:"+b.sayAge()+" 我喜欢的颜色:"+b.colors);
原型式继承
借助原型可以基于已有对象创建新对象
1 function object(obj) { 2 function F(){} 3 F.prototype = obj; //对其进行一次浅复制 4 return new F(); 5 }
寄生式继承(类寄生构造函数模式、工厂模式)
思路:创建一个仅用于封装继承过程的函数
function createAnother(A) { var clone = object(A); //以某种方式来增强这个对象 clone.sayHi = function() { debug(‘Hi’); }; return clone; }
寄生组合式继承
1 //改写原型链继承: 2 function inheritPrototype(subType, superType) { 3 var prototype = object(superType.prototype); //创建对象[超类原型副本] 4 prototype.constructor = subType; //增强对象 5 subType.prototype = prototype; //指定对象 6 } 7 代码: 8 //创建对象的一般步骤[原型式继承] 9 function object(obj) { 10 function F() {} 11 F.prototype = obj; 12 return new F(); 13 } 14 /** 15 * 改写原型链继承 16 * @param {object} subType 子类构造函数 17 * @param {object} superType 超类构造函数 18 */ 19 function inheritPrototype(subType, superType) { 20 var prototype = object(superType.prototype); //创建对象[超类原型副本] 21 prototype.constructor = subType; //增强对象 22 subType.prototype = prototype; //指定对象 23 } 24 function A(name) { 25 this.name = name; 26 this.colors = ['red', 'blue', 'green']; 27 } 28 A.prototype = { 29 sayName: function() { 30 return this.name; 31 } 32 } 33 //借用构造函数实现继承超类属性 34 function B(name, age) { 35 //把A在B的环境中执行,A与B中的this对象指向B的实例 36 A.call(this, name); 37 this.age = age; 38 } 39 //B.prototype = new A(); //原型链继承超类的属性(方法和属性) 40 //寄生组合式继承 41 inheritPrototype(B, A); 42 //此处不能用对象字面量赋值(原因前面已经给出) 43 B.prototype.sayAge = function() { 44 return this.age; 45 } 46 var b = new B('mackxu', 22); 47 debug('我的名字是:'+b.sayName()+" 我的年龄:"+b.sayAge()+" 我喜欢的颜色:"+b.colors);
整理自JavaScript高级程序设计