js繼承的實現(es5)
阿新 • • 發佈:2018-05-16
this 執行 bject 成了 並不會 prototype 函數 實現 PE js對面向對象的支持很弱,所以在ES6之前實現繼承會繞比較多的彎(類似於對面向對象支持弱,然後強行拼湊面向對象的特性)
es5中實現繼承的幾種方式,父類定義為Super
function Super(name){ this.name=name; this.age=25; this.array=[1,2,3]; this.obj={a:‘prop‘}; this.say=function(){ console.log(this.name); } } Super.prototype.testInherit1.構造函數繼承 簡單的在子類構造函數調用父類構造函數,類似就是直接把父類構造函數執行一遍,屬性拷貝一份過來此種繼承方式導致原型鏈斷了,無法實現真正意義上的繼承,=function(){ console.log(‘I am method of super prototype‘) }
child1.testInherit();這個調用會報錯,因為child1並沒有在Super的原型鏈上,導致無法調用其原型的方法;同時因為是拷貝的一份父類的屬性方法,所以子類改動引用類型的屬性並不會影響其他子類的屬性
function Child1(name){ Super.apply(2.使用原型鏈繼承 最基本的思想,把子類的原型設置為父類的實例this,arguments); this.name=name; this.sayName=function(){ console.log(this.name); } } var parent=new Super(‘lucy‘); var child1=new Child1(‘jack‘); var child1=new Child1(‘jack2‘); console.log(parent,child1); console.log(child1.__proto__===Child1.prototype,child1 instanceof Super);//true flase child1.array.push(4); console.log(child1.array,child2.array,s1.array,); child1.testInherit();
function Child2(name){ this.name=name; this.sayName=function(){ console.log(this.name); } } var parent=new Super(‘lucy‘); Child2.prototype=parent; var child1=new Child2(‘jack‘); var child2=new Child2(‘jack2‘); child1.array.push(4); child1.obj.b="prop2"; console.log(child1.array,child2.array,child1.obj,child2.obj); console.log(child1.constructor);修改一個實例繼承的引用屬性,可以看到其他所有實例所繼承的屬性都被修改了(他們引用的都是同一個地址),並且子類實例的構造函數被修改了 前面1和2是繼承的兩種基本模式,也是其他繼承實現的基礎 3.使用組合繼承方式 保證實例繼承屬性私有化並且保證原型鏈不斷
function Child3(name){ Super.apply(this,arguments); this.name=name; } var parent=new Super(‘lucy‘); Child3.prototype=parent; var child1=new Child3(‘jack‘); var child2=new Child3(‘jack2‘); child1.array.push(5); console.log(child1.array,child2.array); console.log(child1.constructor);此種方式可以實現繼承可以保證原型回溯,同時實例繼承的引用類型的屬性互不影響;不過父類的構造函數調用了兩次,子類的實例的構造函數變成了Super,可以做進一步優化 4. 針對上面的組合繼承父類的構造函數調用了2次的改進,可以將子類的原型直接設置為父類的原型,如下所示
function Child4(name){ Super.apply(this,arguments); this.test=44; } Child4.prototype=Super.prototype;//改進父類構造函數調用兩次問題 Child4.prototype.constructor=Child4; var child1=new Child4(‘bob‘); var child2=new Child4(‘bob2‘); console.log(child1.__proto__===Child4.prototype); console.log(child1.__proto__.constructor,‘\n‘,Child4.prototype.constructor,‘\n‘,Super.prototype.constructor); console.log(Super.prototype.constructor); //這種方法改變了父類的構造函數 for(var itm in Child4.prototype){ console.log(itm); }
// 或者使用Object.create()創建一個過渡對象--這樣子類重新定義構造函數,就能使父類和子類各有自己的構造函數 function Child5(name){ Super.apply(this,arguments); this.test=55; } Child5.prototype=Object.create(Super.prototype); Child5.prototype.constructor=Child5; Object.defineProperty(Child5.prototype,‘constructor‘,{//Child5的原型是一個實例對象,所以顯示的定義constructor會改變其不可枚舉的特性,這裏修正一下 enumerable:false }); var child=new Child5(‘end‘); console.log(Child5.prototype.constructor,Super.prototype.constructor); console.log(child instanceof Child5,child instanceof Super); console.log(child.constructor,Child5.prototype.isPrototypeOf(child),Super.prototype.isPrototypeOf(child)); for(var itm in Child5.prototype){ console.log(itm); }5.組合寄生繼承模式(js高程中推薦的實現) 其實這種模式跟上面的第4-2的實現沒有大的區別,不過上邊的中間對象是Object.create()創建的,這裏是自己創建
function inheritProperty(sup,child){ function F(){}; F.prototype=sup.prototype; var inner=new F(); inner.constructor=child; child.prototype=inner; Object.defineProperty(child.prototype,‘constructor‘,{ enumerable:false }); } function Child6(name){ this.age=66; this.name=name; } inheritProperty(Super,Child6); Child6.prototype.sayAge=function(){ return this.age; } var child=new Child6(‘end‘); console.log(child.constructor); console.log(Child6.prototype.constructor); console.log(child.sayAge());
以上繼承都是以1,2為基礎的,具體說實現繼承的方式就這兩種,其他只是對這兩種的組合改造優化
js繼承的實現(es5)