JavaScript 繼承

原型鏈

  JavaScript中實現繼承最簡單的方式就是使用原型鏈,將子類型的原型指向父類型的實例便可,即 「子類型.prototype = new 父類型();」 。app

 

 1 // 爲父類型建立構造函數
 2 function SuperType() {
 3   this.name = ['wuyuchang', 'Jack', 'Tim'];
 4   this.property = true;
 5 }
 6 
 7 // 爲父類型添加方法
 8 SuperType.prototype.getSuerperValue = function() {
 9   return this.property;
10 }
11 
12 // 爲子類型建立構造函數
13 function SubType() {
14   this.test = ['h1', 'h2', 'h3', 'h4'];
15   this.subproperty = false;
16 }
17 
18 // 實現繼承的關鍵步驟,子類型的原型指向父類型的實例
19 SubType.prototype = new SuperType();
20 
21 // 在此處給子類型添加方法,必定要在實現繼承以後,不然會在將指針指向父類型的實例,則方法爲空
22 SubType.prototype.getSubValue = function() {
23   return this.subproperty;
24 }
25 
26 
27 /* 如下爲測試代碼示例 */
28 var instance1 = new SubType();
29 instance1.name.push('wyc');
30 instance1.test.push('h5');
31 alert(instance1.getSuerperValue());    // true
32 alert(instance1.getSubValue());      // false
33 alert(instance1.name);          // wuyuchang,Jack,Tim,wyc
34 alert(instance1.test);          // h1,h2,h3,h4,h5
35 
36 
37 var instance2 = new SubType();
38 alert(instance2.name);          // wuyuchang,Jack,Tim,wyc
39 alert(instance2.test);          // h1,h2,h3,h4

 

原型鏈繼承存在兩個問題:函數

第一,因爲子類型的原型是父類型的實例,也就是子類型的原型中包含的父類型的屬性,從而致使引用類型值的原型屬性會被全部實例所共享。以上代碼的 「instance1.name.push('wyc');" 就說明此問題。學習

第二,在建立子類型的實例時,不能向超類型的構造函數中傳遞參數。所以咱們在實際開發中,不多單獨使用原型鏈。 測試

 

借用構造函數

  爲了解決原型鏈中存在的兩個問題,開發人員開始使用一種叫作 「借用構造函數」 的技術來解決原型鏈中存在的問題。這種技術的實現思路也挺簡單,只須要在子類型的構造函數內調用父類型的構造函數便可。別忘了,函數只不過是在特定環境中執行代碼的對象,所以能夠經過apply()或call()方法執行構造函數。this

 

 1 // 爲父類型建立構造函數
 2 function SuperType(name) {
 3   this.name = name;
 4   this.color = ['pink', 'yellow'];
 5   this.property = true;
 6 
 7   this.testFun = function() {
 8     alert('http://tools.jb51.net/');
 9   }
10 }
11 
12 // 爲父類型添加方法
13 SuperType.prototype.getSuerperValue = function() {
14   return this.property;
15 }
16 
17 // 爲子類型建立構造函數
18 function SubType(name) {
19   SuperType.call(this, name);
20   this.test = ['h1', 'h2', 'h3', 'h4'];
21   this.subproperty = false;
22 }
23 
24 // 在此處給子類型添加方法,必定要在實現繼承以後,不然會在將指針指向父類型的實例,則方法爲空
25 SubType.prototype.getSubValue = function() {
26   return this.subproperty;
27 }
28 
29 
30 /* 如下爲測試代碼示例 */
31 var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);
32 instance1.name.push('hello');
33 instance1.test.push('h5');
34 instance1.color.push('blue');
35 instance1.testFun();            // http://tools.jb51.net/
36 alert(instance1.name);            // wuyuchang,Jack,Nick,hello
37 // alert(instance1.getSuerperValue());    // error 報錯
38 alert(instance1.test);            // h1,h2,h3,h4,h5    
39 alert(instance1.getSubValue());        // false    
40 alert(instance1.color);            // pink,yellow,blue
41 
42 var instance2 = new SubType('wyc');
43 instance2.testFun();            // http://tools.jb51.net/
44 alert(instance2.name);            // wyc    
45 // alert(instance2.getSuerperValue());    // error 報錯
46 alert(instance2.test);            // h1,h2,h3,h4
47 alert(instance2.getSubValue());        // false
48 alert(instance2.color);            // pink,yellow

 

 

能夠看到以上代碼中子類型SubType的構造函數內經過調用父類型 "SuperType.call(this, name);" 實現了屬性的繼承,也能夠在子類型建立實例的時候爲父類型傳遞參數了。但新的問題又來了。能夠看到我在父類型的構造函數中定義了一個方法:testFun,在父類型的原型中定義了一個方法:getSuperValue。但是在實例化子類型後仍然是沒法調用父類型的原型中定義的方法getSuperValue,只能調用父類型中構造函數的方法testFun。這就同建立對象中只使用構造函數模式同樣,使得函數沒有複用性可言。考慮到這些問題,借用構造函數的技術也是不多單獨使用的。spa

 

組合繼承(原型鏈+借用構造函數)

  組合繼承就是結合使用原型鏈與借用構造函數的優勢,組合而成的一個模式。實現也很簡單,既然是結合,那固然結合了兩方的優勢,即原型鏈繼承方法,而在構造函數繼承屬性。.net



 1 // 爲父類型建立構造函數
 2 function SuperType(name) {
 3   this.name = name;
 4   this.color = ['pink', 'yellow'];
 5   this.property = true;
 6 
 7   this.testFun = function() {
 8     alert('http://tools.jb51.net/');
 9   }
10 }
11 
12 // 爲父類型添加方法
13 SuperType.prototype.getSuerperValue = function() {
14   return this.property;
15 }
16 
17 // 爲子類型建立構造函數
18 function SubType(name) {
19   SuperType.call(this, name);
20   this.test = ['h1', 'h2', 'h3', 'h4'];
21   this.subproperty = false;
22 }
23 
24 SubType.prototype = new SuperType();
25 
26 // 在此處給子類型添加方法,必定要在實現繼承以後,不然會在將指針指向父類型的實例,則方法爲空
27 SubType.prototype.getSubValue = function() {
28   return this.subproperty;
29 }
30 
31 
32 /* 如下爲測試代碼示例 */
33 var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);
34 instance1.name.push('hello');
35 instance1.test.push('h5');
36 instance1.color.push('blue');
37 instance1.testFun();            // http://tools.jb51.net/
38 alert(instance1.name);            // wuyuchang,Jack,Nick,hello
39 alert(instance1.getSuerperValue());      // true
40 alert(instance1.test);            // h1,h2,h3,h4,h5    
41 alert(instance1.getSubValue());        // false    
42 alert(instance1.color);            // pink,yellow,blue
43 
44 var instance2 = new SubType('wyc');
45 instance2.testFun();            // http://tools.jb51.net/
46 alert(instance2.name);            // wyc    
47 alert(instance2.getSuerperValue());      // true
48 alert(instance2.test);            // h1,h2,h3,h4
49 alert(instance2.getSubValue());        // false
50 alert(instance2.color);            // pink,yellow

 

 

以上代碼經過 SuperType.call(this, name); 繼承父類型的屬性,經過 SubType.prototype = new SuperType(); 繼承父類型的方法。以上代碼很方便的解決了原型鏈與借用構造函數所遇到的問題,成爲了JavaScript中最爲經常使用的實例繼承的方法。但混合模式也有缺點,能夠看到在以上代碼中在繼承方法的時候實際已經繼承了父類型的屬性,只不過此時對於引用類型屬於共享的,所以在子類型的構造函數內再次調用父類型的構造函數從而繼承了父類型的屬性而去覆蓋了原型中所繼承的屬性,這樣調用兩次構造函數顯然沒有必要,但有什麼方法能夠解決呢?在解決此問題時先看如下兩個模式。prototype

 

原型式繼承:原型式繼承的的實現方法與普通繼承的實現方法不一樣,原型式繼承並無使用嚴格意義上的構造函數,而是藉助原型能夠基於已有的對象建立新對象,同時還沒必要所以建立自定義類型。設計

 

 1 /* 原型式繼承 */
 2 function object(o) {
 3   function F() {}
 4   F.prototype = o;
 5   return new F();
 6 }
 7 
 8 var person = {
 9   name : 'wuyuchang',
10   friends : ['wyc', 'Nicholas', 'Tim']
11 }
12 
13 var anotherPerson = object(person);
14 anotherPerson.name = 'Greg';
15 anotherPerson.friends.push('Bob');
16 
17 var anotherPerson2 = object(person);
18 anotherPerson2.name = 'Jack';
19 anotherPerson2.friends.push('Rose');
20 
21 alert(person.friends);  // wyc,Nicholas,Tim,Bob,Rose

 

寄生式繼承指針

 

 1 /* 原型式繼承 */
 2 function object(o) {
 3   function F() {}
 4   F.prototype = o;
 5   return new F();
 6 }
 7    
 8 /* 寄生式繼承 */
 9 function createAnother(original) {
10   var clone = object(original);
11   clone.sayHi = function() {
12     alert('hi');
13   }
14   return clone;
15 }
16 
17 var person = {
18   name : 'wuyuchang',
19   friends : ['wyc', 'Nicholas', 'Rose']
20 }
21 var anotherPerson = createAnother(person);
22 anotherPerson.sayHi();

 

寄生組合式繼承:前面說過了JavaScrip中組合模式實現繼承的缺點,如今咱們就來解決它的缺點,實現思路是,對於構造函數繼承屬性,而原型鏈的混成形式繼承方法,即不用在繼承方法的時候實例化父類型的構造函數

 

 1 function object(o) {
 2   function F() {}
 3   F.prototype = o;
 4   return new F();
 5 }
 6 
 7 /* 寄生組合式繼承 */
 8 function inheritPrototype(subType, superType) {
 9   var prototype = object(superType.prototype);
10   prototype.constructor = subType;
11   subType.prototype = prototype;
12 }

 

而在使用時只須要將組合模式中的 「SubType.prototype = new SuperType();」 這行代碼替換成 inheritPrototype(subType, superType);  便可。寄生組合式繼承的高效率體如今它只調用了一次父類型構造函數,避免了建立沒必要要的或多餘的屬性。與此同時,原型鏈還能保持不變。所以,還可以正常使用instanceof和isPrototypeof()。這也是目前來講最理想的繼承方式了,目前也在向這種模式轉型。

 

文章學習自http://www.jb51.net/article/53824.htm有刪改。

 

 

 

最終參考是來自於《JavaScript高級程序設計》一書。

相關文章
相關標籤/搜索