如果还不知道js的构造函数,请先看js 面向对象学习(构造函数篇)。
我们知道继承一般有两种方式:
1.接口继承:只继承方法签名。
2.实现继承:继承实际方法。
JS没有类,函数无签名,只能使用实现继承。
1.原型链:
原理:利用原型,将一个引用类型继承另一个引用类型的方法和属性。
缺点: 1.若是包含引用类型的原型,则此引用类型将被所有实例(包括继承的实例)共享。
2.创建子类型时,不能向超类型构造函数中传递参数。
例子:
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } // 子对象 function SubType(){ this.subproperty = false; } // 继承SuperType的实例,不能直接等于父对象原型,这要会共享,一改动全都改了 SubType.prototypw = new SuperType(); SubType.prototype.getSubValue = function() { return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); // => true
看下图了解原型链搜索过程:(当用实例获取属性或方法时) 注意:Object也是一个道理。
1.直接搜索instance实例
2.搜索子对象的原型,如:SubType.prototype
3.搜索父对象的原型,如:SuperType.prototype(通过父对象实例找到)
特别注意:此时instance的constructor是指向SuperType的,因为SubType.protoype=new SuperType()已经将constructor重写了。所以,不要使用对象字面量来添加方法,这样会重写原型链。如:SuperType = { getSubValue: function() {} }
2.借用构造函数:
原理:在子类构造函数内部调用超类构造函数。(call()/apply())
优点:1.可以向超类传递参数. 2.不会共享引用类型,而是产生一个副本属性。
缺点:由于超类的原型定义的方法,子类是看不见的,所以所有类型只能用构造函数模式。
例子:
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { // 继承父类SuperType SuperType.call(this); // 此时,用SuperType函数替换SubType构造函数。 } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); var instance2 = new SuperType(); alert(instance1.colors);
3.组合继承:
原理:将借用构造函数与原型链结合使用。
1.第一次调用原型等于超类实例就调用一次超类的构造函数得到其属性。
2.第二次调用超类构造函数是新对象上创建属性,屏蔽原本的属性(这也是引用类型不共享的原因)。
优点:可共享方法,可传参数,引用类型不会混乱。
缺点:调用两次超类构造函数
例子:
function SuperType(name) { this.name = name; this.colors = ['red', 'blue', 'green']; } SuperType.prototype.sayName = function() { alert(this.name); }; function SubType(name, age) { // 继承属性 SuperType.call(this, name); // 第2次调用超类构造函数 this.age = age; } // 继承方法 SubType.prototype = new SuperType(); // 第1次调用超类构造函数 SubType.prototype.sayAge = function() { alert(this.age); } var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); instance1.sayName(); instance1.sayAge(); var instance2 = new SubType("CKJ", 22); alert(instance2.colors); instance2.sayName(); instance2.sayAge();
4.原型式继承:
原理:传入实例,返回子对象实例.(像产子)
优点:实现面向对象封装过程。
缺点:必须有一个对象作为另一个对象的基础。
例子:
function object(o) { function F() {}; // 声明构造函数 F.prototype = o; // 继承传入的实例对象 return new F(); // 返回子类实例 }
注意:ECMAScript5新增了Object.create(对象名, {
对象字面量
})用来规范化了原型式继承。
5.寄生式继承:
原理:调用原型式继承,再实现继承,并封装在函数中。
优点:进一步实现封装。
缺点:不能做到函数复用而低效率。
例子:
function createObject(original) { var clone = object(original); // 产生子类对象 clone.sayHi = function() { // 增强子类对象 alert("Hello, CKJ!"); }; return clone; // 返回子类对象 }
6.寄生组合继承:
原理:利用构造函数继承属性,原型链继承方法。
优点:只需超类原型的副本
例子:
function inheritPrototype(subType, superType) { var proto = object(superType.prototype); // 产生父类原型的副本的实例 proto.constructor = subType; // 查看原型式,F.prototype = superType.prototype, constructor被破坏了 subType.prototype = proto; // 指定对象。 }
YAHOO.lang.extend()采用寄生组合继承.