繼承方式主要有六種:
1、原型鏈繼承 存在物件共享的問題
2、建構子繼承 借助call apply方法實現 :函數復用性問題每次實例化,重新執行了一次父類,父類別中的方法重複定義
3、組合模式(原型鏈+建構子)建構函式繼承屬性,原型鏈繼承方法 :解決了物件共享,但是屬性繼承出現兩次
4、原型方式使用一個空間空函數
5、寄生方式 在原型方式上增加額外了方法
6、寄生組合方式(解決了屬性繼承出現2次的問題)
1、原型鏈方式
子類別在繼承父類時,是將子類別的prototype指向父類別的一個實例物件。 當子類別實例化時,所有子類別的實例都會共用父類別的那個實例上(假設叫做p)的屬性和方法,如果p上有引用型的屬性,則子類別的實例可以修改該引用型的屬性,導致其他實例上的該屬性也被修改。
//原型链方式 function A(){ this.name = 'lc'; this.stus = ['a','b','c']; } A.prototype.introduce = function (){ alert(this.name); } function B(){} B.prototype = new A(); var s1 = new B(); var s2 = new B(); console.log(s1.stus); //['a','b','c'] s1.stus.push('d'); //改变s1上的属性,会影响s2上的该属性 console.log(s2.stus); // ['a','b','c','d']
如果改成下面的,則不會影響。
//原型链方式 function A(){ this.name = 'lc'; this.stus = ['a','b','c']; } A.prototype.introduce = function (){ alert(this.name); } function B(){} B.prototype = new A(); var s1 = new B(); var s2 = new B(); s1.stus = ['d']; //在s1上增加了属性, 就近原则,不在查找原型链上的同名属性 console.log(s1.stus); //['d'] console.log(s2.stus); // ['a','b','c']
2、構造函數方式繼承
共用構造函數,方法都是在內部定義,複函復用性問題無從談起。
//构造函数方式继承 function A(name){ this.name = name; this.type = 'parent'; this.introduce = function (){ alert(this.name);} } A.prototype.sayhi = function (){ alert(this.type); } function B(name,age){ A.call(this,name); //使用call方法 实现继承 但是父类中的方法重复定义 无复用性 this.age = age; this.introduce = function (){ alert(this.name+this.age);} } var b = new B('lc',25); b.introduce(); //lc25 b.sayhi(); //parent
3、混合方式(原型方式+構造函數方式)
使用構造函數繼承屬性,原型鏈繼承方法
但是有一點不好的地方: 使用原型鏈繼承時,實際上屬性也繼承了,重複了。
function A(name){ this.name = name; this.type = 'parent'; } A.prototype.sayhi = function (){ alert(this.type); } function B(name,age){ A.call(this,name); this.age = age; this.introduce = function (){ alert(this.name+this.age);} } B.prototype = new A();
4、原型式繼承
使用了一個中間空函數,實現繼承後,回傳一個物件。
function extend(parent){ function F(){} F.prototype = parent; return new F(); }
5、寄生式繼承 在原型式的基礎上,再為物件添加屬性方法
function extend(parent){ function F(){} F.prototype = parent; return new F(); } function createObj(p){ var clone = extend(p); clone.name = 'hello'; clone.say = function (){} return clone; }
6、寄生組合式繼承
在繼承方法的時候,不再實例化父類型的建構函數,而是使用inheritPrototype方法. 使用一個中間空函數,讓這個空函數去繼承父類別原型,然後實例化這個空函數(繼承了父類別prototype中的所有方法)。將子類別的原型指向這個實例化的空物件即可。
避免了實例化父類別的建構子。
/* 寄生组合式继承 */ function inheritPrototype(subType, superType) { // var obj= extend(superType.prototype); function F(){} F.prototype = superType.prototype; var obj= new F(); //obj.constructor = subType; subType.prototype = obj; //不可将subType的prototype直接指向superType,否则的话,对子类prototype的修改会反应到父类上, 引用型变量 subType.prototype.constructor = subType; }
物件冒充: 使用建構子的方式宣告類,將一個類別的方法指向另一個建構函式(因為建構子本身就是一個方法函式),實作繼承
function A(){ this.name = {name1:'hello'}; this.say = function (){ console.log(this.name);} } function B(){ this.method = A; this.method(); delete this.method; } var bObj = new B();