一、你必須知道的
1> 原型及原型鏈在繼承中起到了關鍵的作用。所以你一定要理解他們。
2> 不會JS中的OOP,你也太菜了吧!(第一篇)
二、繼承的6種方法
1> 原型鏈繼承
原型鏈繼承是通過創建Super的實例,並將該實例賦值給Sub.prototype來實現的。
實現的本質是:重寫子類型的原型對象,代之以超類型的實例。
function Super(){ this.name = 'JChen___'; } Super.prototype.getSuperName = function(){ return this.name; } function Sub(){ this.subname = 'JChen___son'; } Sub.prototype = new Super(); //原型繼承體現在這里 Sub.prototype.getSubName = function(){ return this.subname; } var instance = new Sub();
注意:此時instance.constructor現在指向的是Super的,這是因為Sub.prototype指向了Super.prototype,而Super.prototype.constructor = Super。
原型鏈的問題:類似於利用原型創建對象,原型共享的特性也是原型鏈繼承的最大問題。
2> 借用構造函數繼承
在解決原型中包含引用類型值所帶來的問題的過程中,我們開始使用一種叫做借用構造函數的技術。
這種技術的基本思想相當簡單:在子類型構造函數的內部調用超類型構造函數。
這樣一來,就會在新子類對象上執行超類函數中定義的所有對象初始化代碼。結果,每個子類的實力都會有自己的超類中屬性的副本了。
function Super2(name){ this.colors = ['red', 'blue']; this.name = name; } function Sub2(){ Super2.call(this, 'JChen___2'); //借用構造函數技術體現在這里 this.age = 29; } var instance1 = new Sub2(); instance1.colors.push('black'); var instance2 = new Sub2(); instance2.colors.push('green');
借助構造函數繼承的問題:
1): 方法都在構造函數中定義,無法復用。
2): 在超類型的原型中的方法對子類是不可見的。
3> 組合繼承(原型+借用構造)
組合繼承指的是將原型鏈和借用構造函數的技術組合到一塊,從而發揮二者之長的一種繼承模式。
組合繼承的思路:使用原型鏈實現對方法和屬性的繼承,通過借用構造函數實現對實例屬性的繼承。
function Super3(name){ this.name = name; this.colors = ['red', 'blue']; } Super3.prototype.sayName = function(){ return this.name; } function Sub3(name, age) { Super3.call(this, name); this.age = age; } Sub3.prototype = new Super3(); //解決借用構造函數技術的缺點 Sub3.prototype.constructor = Sub3; //糾正原型繼承改變了的構造函數 Sub3.prototype.sayAge = function(){ return this.age; }
組合繼承避免了原型鏈和借用構造函數的缺陷,融合了他們的優點,成為JavaScript中最常用的繼承模式。
組合繼承的問題:兩次調用超類構造函數。
4> 原型式繼承
原型式繼承的思路:借助原型可以基於已有的對象創建新對象,同時還不必因此創建自定義類型。
function object(o){ //原型式繼承的關鍵 function F(){} F.prototype = o; return new F(); } var person = { name: 'JChen___4', colors: ['blue'] } var person1 = object(person); person1.name = 'JChen___4___2' person1.colors.push('red'); var person2 = object(person); person2.name = 'JChen___4___3'; person2.colors.push('green');
原型式繼承的問題:同原型鏈一樣,他也有共享的劣勢。
5> 寄生式繼承
寄生式繼承的思路:創建一個僅用於封裝繼承過程的函數,該函數內部以某種方式來增強對象,最后再返回該對象
function createAnother(origin){ //寄生式繼承的關鍵 var clone = object(origin); clone.sayHi = function(){ return 'Hi'; }; return clone; } var person = { name: 'JChen___4', colors: ['blue'] } var person1 = createAnother(person);
寄生式繼承的問題:像構造函數一樣,由於不能做到函數的復用而降低效率。
6> 寄生組合式繼承
寄生組合式繼承:通過借用構造函數來借用屬性,通過原型鏈的混成形式來繼承方法。
其背后的思想是:不必為了指定子類型的原型而調用超類型的構造函數,我們需要的無非就是超類型的一個副本而已。
function object(o){ function F(){} F.prototype = o; return new F(); } function inheritProto(subType, superType){ //避免第一調用構造函數的關鍵 var proto = object(superType.prototype); proto.constructor = subType; subType.prototype = proto; } function Super6(name){ this.name = name; this.colors = ['red', 'blue']; } Super6.prototype.sayName = function(){ return this.name; } function Sub6(name, age){ Super6.call(this, name); this.age = age; } inheritProto(Sub6, Super6); Sub6.prototype.sayAge = function(){ return this.age; } var instance1 = new Sub6('JChen___6', '12'); instance1.colors.push('black');
開發人員普遍認為寄生組合式繼承是引用類型最理想的繼承范式。
三、總結
這就是JavaScript中的6種繼承方式,如果大家能夠畫出每個繼承的原型鏈關系圖,那么繼承就是小菜一碟了。