成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一文完全吃透JavaScript繼承(面試必備良藥)

開發(fā) 前端
本文全面總結了JavaScript 中的繼承類型、各個繼承類型的優(yōu)缺點及使用場景等,一文吃透 JavaScript 繼承,收藏起來吧~

 

背景

繼承給我們提供了一種優(yōu)雅可復用的編碼方式,繼承也是面試中經(jīng)常被問到的問題,本文全面總結了JavaScript 中的繼承類型、各個繼承類型的優(yōu)缺點及使用場景等,一文吃透 JavaScript 繼承,收藏起來吧~

原型繼承

原型鏈是實現(xiàn)原型繼承的主要方法,基本思想就是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。

實現(xiàn)原型鏈的基本模式 

  1. function SuperType(){  
  2.  this.property=true 
  3.  
  4. SuperType.prototype.getSuperValue=function(){  
  5.   returnthis.property; 
  6.  
  7. function SubType(){  
  8.   this.subproperty=false 
  9.  
  10. SubType.prototype=new SuperType(); 
  11. SubType.prototype.getSubValue=function(){  
  12.      returnthis.property;  
  13. };  
  14. var instance=new SubType();  
  15. console.log(instance.getSuperValue()); //true; 

例子中的實例及構造函數(shù)和原型之間的關系圖:

在例子代碼中,定義了兩個對象,subType和superType。

兩個對象之間實現(xiàn)了繼承,而這種繼承方式是通過創(chuàng)建SuperType的實例并將該實例賦給subType.prototype實現(xiàn)的。實現(xiàn)的本質就是重寫了原型對象。

這樣subType.prototype中就會存在一個指針指向superType的原型對象。也就是說,存在superType的實例中的屬性和方法現(xiàn)在都存在于subType.prototype中了。這樣繼承了之后,又可以為subType添加新的方法和屬性。

要注意,這個指針([[prototype]])默認情況下是不可以再被外部訪問的,估計是會被一些內部方法使用的,例如用for...in來遍歷原型鏈上可以被枚舉的屬性的時候,就需要通過這個指針找到當前對象所繼承的對象。不過,F(xiàn)irefox、Safari和Chrome在每個對象上都支持一個屬性__proto__。

原型繼承需要注意的一些問題

1. 別忘記默認的類型

我們知道,所有的引用類型都繼承了Object,而這個繼承也是通過原型鏈實現(xiàn)的。所以所有的對象都擁有Object具有的一些默認的方法。如:hasOwnProperty()、propertyIsEnumerable()、toLocaleString()、toString()和valueOf()

2. 確定原型和實例的關系可以通過兩種方式來確定原型和實例之間的關系。

① 使用instanceof 操作符,只要用這個操作符來測試實例與原型鏈中出現(xiàn)過的構造函數(shù),結果就會返回true。

② 第二種方式是使用isPrototypeOf()方法。同樣,只要是原型鏈中出現(xiàn)過的原型,都可以說是該原型鏈所派生的實例的原型,因此isPrototypeOf()方法也會返回true。

例子: 

  1. alert(instance instanceofObject); //true  
  2. alert(instance instanceof SuperType); //true  
  3. alert(instance instanceof SubType); //true  
  4. alert(Object.prototype.isPrototypeOf(instance)); //true  
  5. alert(SuperType.prototype.isPrototypeOf(instance)); //true  
  6. alert(SubType.prototype.isPrototypeOf(instance)); //true 

③ 子類要在繼承后定義新方法

因為,原型繼承是實質上是重寫原型對象。所以,如果在繼承前就在子類的prototype上定義一些方法和屬性。那么繼承的時候,子類的這些屬性和方法將會被覆蓋。

如圖:

④ 不能使用對象字面量創(chuàng)建原型方法

這個的原理跟第三點的實際上是一樣的。當你使用對象字面量創(chuàng)建原型方法重寫原型的時候,實質上相當于重寫了原型鏈,所以原來的原型鏈就被切斷了。如圖:

⑤ 注意父類包含引用類型的情況

如圖:

這個例子中的SuperType 構造函數(shù)定義了一個colors 屬性,該屬性包含一個數(shù)組(引用類型值)。SuperType 的每個實例都會有各自包含自己數(shù)組的colors 屬性。當SubType 通過原型鏈繼承了SuperType 之后,SubType.prototype 就變成了SuperType 的一個實例,因此它也擁有了一個它自己的colors 屬性——就跟專門創(chuàng)建了一個SubType.prototype.colors 屬性一樣。但結果是什么呢?結果是SubType 的所有實例都會共享這一個colors 屬性。而我們對instance1.colors 的修改能夠通過instance2.colors 反映出來。也就是說,這樣的修改會影響各個實例。

原型繼承的缺點(問題)

  1.  最明顯的就是上述第⑤點,有引用類型的時候,各個實例對該引用的操作會影響其他實例。
  2.  沒有辦法在不影響所有對象實例的情況下,給超類型的構造函數(shù)傳遞參數(shù)。

有鑒于此,實踐中很少會單獨使用原型繼承。

借用構造函數(shù)繼承

在解決原型中包含引用類型值所帶來問題的過程中,開發(fā)人員開始使用一種叫做借用構造函數(shù) (constructor stealing)的技術(有時候也叫做偽造對象或經(jīng)典繼承)。這種技術的基本思想相當簡單,即 在子類型構造函數(shù)的內部調用超類型構造函數(shù)。

基本模式 

  1. function SuperType(){  
  2.   this.colors = ["red", "blue", "green"];  
  3.  
  4. function SubType(){  
  5.    //繼承了SuperType  
  6.   SuperType.call(this);  
  7.  
  8. var instance1 = new SubType();  
  9. instance1.colors.push("black");  
  10. alert(instance1.colors); //"red,blue,green,black"  
  11. var instance2 = new SubType();  
  12. alert(instance2.colors); //"red,blue,green" 

基本思想

借用構造函數(shù)的基本思想就是利用call或者apply把父類中通過this指定的屬性和方法復制(借用)到子類創(chuàng)建的實例中。因為this對象是在運行時基于函數(shù)的執(zhí)行環(huán)境綁定的。也就是說,在全局中,this等于window,而當函數(shù)被作為某個對象的方法調用時,this等于那個對象。call 、apply方法可以用來代替另一個對象調用一個方法。call、apply 方法可將一個函數(shù)的對象上下文從初始的上下文改變?yōu)橛?thisObj 指定的新對象。

所以,這個借用構造函數(shù)就是,new對象的時候(注意,new操作符與直接調用是不同的,以函數(shù)的方式直接調用的時候,this指向window,new創(chuàng)建的時候,this指向創(chuàng)建的這個實例),創(chuàng)建了一個新的實例對象,并且執(zhí)行SubType里面的代碼,而SubType里面用call調用了SuperTyep,也就是說把this指向改成了指向新的實例,所以就會把SuperType里面的this相關屬性和方法賦值到新的實例上,而不是賦值到SupType上面。所有實例中就擁有了父類定義的這些this的屬性和方法。

優(yōu)勢

相對于原型鏈而言,借用構造函數(shù)有一個很大的優(yōu)勢,即可以在子類型構造函數(shù)中向超類型構造函數(shù)傳遞參數(shù)。因為屬性是綁定到this上面的,所以調用的時候才賦到相應的實例中,各個實例的值就不會互相影響了。

例如: 

  1. function SuperType(name){  
  2.     this.name = name;  
  3.  
  4. function SubType(){  
  5.     //繼承了SuperType,同時還傳遞了參數(shù)  
  6.     SuperType.call(this, "Nicholas");  
  7.     //實例屬性  
  8.     this.age = 29 
  9.  
  10. var instance = new SubType();  
  11. alert(instance.name); //"Nicholas";  
  12. alert(instance.age); //29 

劣勢

如果僅僅是借用構造函數(shù),那么也將無法避免構造函數(shù)模式存在的問題——方法都在構造函數(shù)中定義,因此函數(shù)復用就無從談起了。而且,在超類型的原型中定義的方法,對子類型而言也是不可見的,結果所有類型都只能使用構造函數(shù)模式。考慮到這些問題,借用構造函數(shù)的技術也是很少單獨使用的。

組合繼承

組合繼承(combination inheritance),有時候也叫做偽經(jīng)典繼承。是將原型鏈和借用構造函數(shù)的技術組合到一塊,從而發(fā)揮二者之長的一種繼承模式。

基本思想

思路是使用原型鏈實現(xiàn)對原型屬性和方法的繼承,而通過借用構造函數(shù)來實現(xiàn)對實例屬性的繼承。這樣,既通過在原型上定義方法實現(xiàn)了函數(shù)復用,又能夠保證每個實例都有它自己的屬性。

基本模型 

  1. function SuperType(name){  
  2.   this.name = name;  
  3.   this.colors = ["red", "blue", "green"];  
  4.  
  5. SuperType.prototype.sayName = function(){  
  6.    alert(this.name);  
  7. };  
  8. function SubType(name, age){  
  9. //繼承屬性  
  10.   SuperType.call(this, name);  
  11.   this.age = age;  
  12.  
  13. //繼承方法  
  14. SubType.prototype = new SuperType();  
  15. SubTypeSubType.prototype.constructor = SubType;  
  16. SubType.prototype.sayAge = function(){  
  17.     alert(this.age);  
  18. };  
  19. var instance1 = new SubType("Nicholas", 29);  
  20. instance1.colors.push("black");  
  21. alert(instance1.colors); //"red,blue,green,black"  
  22. instance1.sayName(); //"Nicholas";  
  23. instance1.sayAge(); //29  
  24. var instance2 = new SubType("Greg", 27);  
  25. alert(instance2.colors); //"red,blue,green"  
  26. instance2.sayName(); //"Greg";  
  27. instance2.sayAge(); //27 

優(yōu)勢

組合繼承避免了原型鏈和借用構造函數(shù)的缺陷,融合了它們的優(yōu)點,成為JavaScript 中最常用的繼承模式。

劣勢

組合繼承最大的問題就是無論什么情況下,都會調用兩次超類型構造函數(shù):一次是在創(chuàng)建子類型原型的時候,另一次是在子類型構造函數(shù)內部。雖然子類型最終會包含超類型對象的全部實例屬性,但我們不得不在調用子類型構造函數(shù)時重寫這些屬性。

寄生類繼承

原型式繼承

其原理就是借助原型,可以基于已有的對象創(chuàng)建新對象。節(jié)省了創(chuàng)建自定義類型這一步(雖然覺得這樣沒什么意義)。

模型 

  1. function object(o){  
  2.   function W(){  
  3.   }  
  4.   W.prototype = o;  
  5.  returnnew W(); 
  6.  

ES5新增了Object.create()方法規(guī)范化了原型式繼承。即調用方法為:Object.create(o);

適用場景

只想讓一個對象跟另一個對象建立繼承這種關系的時候,可以用Object.create();這個方法,不兼容的時候,則手動添加該方法來兼容。

寄生式繼承

寄生式繼承是原型式繼承的加強版。

模型 

  1. function createAnother(origin){  
  2.   var clone=object(origin);  
  3.   clone.say=function(){  
  4.     alert('hi')  
  5.   }  
  6.   return clone; 
  7.  

即在產(chǎn)生了這個繼承了父類的對象之后,為這個對象添加一些增強方法。

寄生組合式繼承

實質上,寄生組合繼承是寄生式繼承的加強版。這也是為了避免組合繼承中無可避免地要調用兩次父類構造函數(shù)的最佳方案。所以,開發(fā)人員普遍認為寄生組合式繼承是引用類型最理想的繼承范式。

基本模式 

  1. function inheritPrototype(SubType,SuperType){  
  2.   var prototype=object(SuperType.prototype);  
  3.   prototype.constructor=subType 
  4.   subType.prototype=prototype;  

這個object是自定義的一個相當于ES5中Object.create()方法的函數(shù)。在兼容性方面可以兩個都寫。

兼容寫法 

  1. function object(o){  
  2.     function W(){  
  3.     }  
  4.     W.prototype=o;  
  5.     returnnew W;  
  6.  
  7. function inheritPrototype(SubType,SuperType){  
  8.     var prototype;  
  9.    if(typeofObject.create==='function'){  
  10.     prototype=Object.create(SuperType.prototype);  
  11.    }else{  
  12.     prototype=object.create(SuperType.prototype);  
  13.    }<br>           prototype.constructor=SubType 
  14.    SubType.prototype=prototype;  

Class繼承

Class 可以通過extends關鍵字實現(xiàn)繼承。子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類自己的this對象,必須先通過父類的構造函數(shù)完成塑造,得到與父類同樣的實例屬性和方法,然后再對其進行加工,加上子類自己的實例屬性和方法。如果不調用super方法,子類就得不到this對象。

注意 :ES5 的繼承,實質是先創(chuàng)造子類的實例對象this,然后再將父類的方法添加到this上面(Parent.apply(this))。ES6 的繼承機制完全不同,實質是先將父類實例對象的屬性和方法,加到this上面(所以必須先調用super方法),然后再用子類的構造函數(shù)修改this。 

  1. class ColorPoint extends Point {  
  2.   constructor(x, y, color) {  
  3.     super(x, y); // 調用父類的constructor(x, y)  
  4.     this.color = color;  
  5.   }  
  6.   toString() {  
  7.     returnthis.color + ' ' + super.toString(); // 調用父類的toString()  
  8.   }  

Class的繼承鏈

大多數(shù)瀏覽器的 ES5 實現(xiàn)之中,每一個對象都有__proto__屬性,指向對應的構造函數(shù)的prototype屬性。Class 作為構造函數(shù)的語法糖,同時有prototype屬性和__proto__屬性,因此同時存在兩條繼承鏈。

(1)子類的__proto__屬性,表示構造函數(shù)的繼承,總是指向父類。

(2)子類prototype屬性的__proto__屬性,表示方法的繼承,總是指向父類的prototype屬性。 

  1. class A {  
  2.  
  3. class B extends A {  
  4.  
  5. B.__proto__ === A // true  
  6. B.prototype.__proto__ === A.prototype // true 

上面代碼中,子類B的__proto__屬性指向父類A,子類B的prototype屬性的__proto__屬性指向父類A的prototype屬性。 

 

責任編輯:龐桂玉 來源: 前端大全
相關推薦

2024-09-18 13:57:15

2021-04-27 11:28:21

React.t事件元素

2025-02-03 07:00:00

Java接口工具

2023-08-27 21:29:43

JVMFullGC調優(yōu)

2024-08-09 08:41:14

2021-11-02 10:53:56

Linux機制CPU

2024-08-26 08:58:50

2025-06-05 03:11:00

2022-08-15 15:39:23

JavaScript面向對象數(shù)據(jù)

2021-01-26 05:19:56

語言Go Context

2025-04-28 01:22:45

2021-08-30 19:04:29

jsIO

2024-10-11 09:27:52

2025-04-09 05:22:00

2024-08-09 12:44:45

JavaScript原型鏈鏈條

2021-09-10 16:10:21

panda透視表語言

2023-02-28 18:09:53

Javascript定時器

2025-03-03 08:40:00

JavaScriptthis開發(fā)

2023-02-23 19:32:03

DOMJavascript開發(fā)

2021-10-11 10:19:48

Javascript 高階函數(shù)前端
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本高清不卡视频 | 狠狠操狠狠操 | 亚洲欧美视频在线观看 | www.日韩系列| 亚洲一区视频在线 | 青青草视频网 | 欧美一级欧美三级在线观看 | 久久久久久久91 | 麻豆精品国产91久久久久久 | 亚洲网站在线观看 | 久久亚洲视频 | 欧美日韩在线一区二区 | 国产粉嫩尤物极品99综合精品 | 中文字幕国产一区 | 欧美伊人久久久久久久久影院 | 高清国产午夜精品久久久久久 | 亚洲人成网亚洲欧洲无码 | 中文字幕亚洲一区二区三区 | 成人免费大片黄在线播放 | 久草免费在线视频 | 人人射人人插 | 国产在线激情视频 | 天天操网| 6996成人影院网在线播放 | 一区二区精品 | 夜夜摸天天操 | 久国久产久精永久网页 | 精品99爱视频在线观看 | 国产aⅴ | 成人在线看片 | 亚洲精品视频在线 | 中文字幕av网站 | 日韩在线视频一区二区三区 | 麻豆av片 | 视频一区二区国产 | 动漫www.被爆羞羞av44 | 日本精品一区 | 中文字幕成人在线 | 情侣av| 久久精品日产第一区二区三区 | 在线色网址 |