欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文詳解如何用原型鏈的方式實現(xiàn)JS繼承

 更新時間:2022年04月15日 10:05:48   作者:前端西瓜哥  
JavaScript中,每當(dāng)創(chuàng)建一個對象,都會給這個對象提供一個內(nèi)置對象 [[Prototype]] 。這個對象就是原型對象,[[Prototype]] 的層層嵌套就形成了原型鏈。本文將詳細(xì)講解如何用原型鏈的方式實現(xiàn)一個 JS 繼承,感興趣的可以了解下

今天講一道經(jīng)典的原型鏈面試題。

原型鏈?zhǔn)鞘裁?/h2>

JavaScript 中,每當(dāng)創(chuàng)建一個對象,都會給這個對象提供一個內(nèi)置對象 [[Prototype]] 。這個對象就是原型對象,[[Prototype]] 的層層嵌套就形成了原型鏈。

當(dāng)我們訪問一個對象的屬性時,如果自身沒有,就會通過原型鏈向上追溯,找到第一個存在該屬性原型對象,取出對應(yīng)值。

當(dāng)然原型鏈不是無止境的,和單鏈表一樣,最后一個原型對象的值是 null,原型鏈的所有對象都找不到指定的屬性時,我們會拿到 undefined。

[[Prototype]] 雖然無法通過腳本進(jìn)行訪問,但大多數(shù)瀏覽器提供了 __proto__ 屬性來訪問這個內(nèi)置對象,但它并不是標(biāo)準(zhǔn),無法兼容所有瀏覽器。

下面來舉幾個例子,讓讀者對原型鏈有一個直觀的認(rèn)識:

  • 通過對象字面量聲明 a = {} 時, a 的 [[prototype]] 就是 Object.prototype。此時的原型鏈?zhǔn)牵篴 -> Object.prototype -> null。這里有個易錯點,就是以為 a 的上一個原型對象是 Object,其實并不對,Object 其實只是一個構(gòu)造函數(shù)。
  • 聲明數(shù)組 arr = [1, 2, 4],它的原型鏈則是 arr -> Array.prototype -> Object.prototype -> null。
  • Object.create(null) 甚至能夠創(chuàng)建一個連 [[prototype]] 都沒有的真正的空對象,一般用于做字符串哈希表,比如 vue 源碼里就能經(jīng)常看到。

通過構(gòu)造函數(shù)創(chuàng)建實例對象

在 JavaScript 中,一個函數(shù)會在 new 關(guān)鍵字的配合下成為構(gòu)造函數(shù)。也就是說,任何一個函數(shù)都可以成為構(gòu)造函數(shù)。

當(dāng)聲明一個構(gòu)造函數(shù)時,它會有一個屬性名為 prototype 的對象(和 [[prototype]] 是不同的東西),這個對象就是 原型對象。這個對象的 constructor 又反過來指向構(gòu)造函數(shù)。

當(dāng)我們對使用 new 關(guān)鍵字創(chuàng)建對象,被創(chuàng)建的對象的 [[prototype]] 會指向這個 prototype。

function Rect() {}
const rect = new Rect()
rect.__proto__ === Rect.prototype // true
Rect.prototype.constructor === Rect // true

只要是通過 new Rect() 創(chuàng)建的對象,無論多少次,它的 [[prototype]] 都是指向 Rect.prototype。另外,Rect.prototype.prototype 指向的是 Object.prototype。

這樣,通過給構(gòu)造函數(shù)的原型對象(Rect.prototype)添加一些方法(如 Rect.prototype.draw),就能讓創(chuàng)建的多個實例對象共享同一個方法,減少內(nèi)存的使用。

用原型鏈的方式實現(xiàn)繼承

理解了構(gòu)造函數(shù)如何影響創(chuàng)建的實例的原型鏈后,我們來探討一下核心問題,如何使用原型鏈來實現(xiàn)繼承。

假設(shè)我們有一個 Shape 構(gòu)造函數(shù)(父類)和 Rect 構(gòu)造函數(shù)(子類)。代碼如下:

// 父類
function Shape() {}
Shape.prototype.draw = function() {
  console.log('Shape Draw')
}
Shape.prototype.clear = function() {
  console.log('Shape Clear')
}
// 子類
function Rect() {}
/** 
 實現(xiàn)繼承的代碼放這里
**/
Rect.prototype.draw = function() {
  console.log('Rect Draw')
}

通過前面的學(xué)習(xí),我們知道,正常情況下使用 new Rect 創(chuàng)建的實例對象,它的原型鏈?zhǔn)沁@樣的:

rect -> Rect.prototype -> Object.protoype -> null

現(xiàn)在我們要實現(xiàn)的繼承,其實就是在原型鏈中間再加一個原型對象 Shape.prototype。對此我們需要對 Rect.prototype 進(jìn)行特殊的處理。

方法1:Object.create

Rect.prototype = Object.create(Shape.prototype)
Rect.prototype.constructor = Rect // 選用,如果要用到 constructor

Rect.prototype.constructor = Rect // 選用,如果要用到 constructor

Object.create(proto) 是個神奇的方法,它能夠創(chuàng)建一個空對象,并設(shè)置它的 [[prototype]] 為傳入的對象。

因為我們無法通過代碼的方式給 [[prototype]] 屬性賦值,所以使用了 Object.create 方法作為替代。

因為 Rect.prototype 指向了另一個新的對象,所以把 constructor 給丟失了,可以考慮把它放回來,如果你要用到的話。

缺點是替換掉了原來的對象。

方法2:直接修改 [[prototype]]

如果就是不想使用新對象,只想修改原對象,可以使用 廢棄 的 __proto__ 屬性,但不推薦。

不過另外還有一個方法 Object.setPrototypeOf() 可以修改對象的 [[prototype]],但因為性能的問題,也不推薦使用。

Object.setPrototypeOf(Rect.prototype, Shape.prototype)
// 或 
Rect.prototype.__proto__ = Shape.prototype

都不推薦使用,但確實能用。

方法3:使用父類的實例

Rect.prototype = new Shape()

形成的原型鏈為:

rect -> shape(替代掉原來的 Rect.prototype) -> Shape.prototype -> Object.prototype -> null

基本能用,缺點是會產(chǎn)生副作用,就是執(zhí)行 new Shap() 可能會出現(xiàn)副作用,比如給創(chuàng)建的對象添加了一些屬性、發(fā)送了請求之類的,完全取決于構(gòu)造函數(shù)內(nèi)的代碼。

某種意義上,這個缺點是致命的。不推薦使用。

總結(jié)

用原型鏈的方式實現(xiàn)一個 JS 繼承,其實就是希望構(gòu)造函數(shù) Son 創(chuàng)建出來的對象 son,它的原型鏈上加上父類 Parent.prototype,所以最后就是要修改 Son.prototype 的 [[prototype]]。

鑒于性能、兼容性、副作用等考慮,推薦使用方法 1,即通過 Object.create(Parent.prototype) 創(chuàng)建一個指定了 [[prototype]] 的新對象,替換掉原來的 Son.prototype 指向的對象。

總結(jié)幾個核心知識點:

  • 任何對象都有 [[prototype]] 屬性,讀寫對象屬性發(fā)現(xiàn)當(dāng)前對象不存在時,會訪問 [[prototype]] 指向的對象嘗試訪問屬性,于是原型鏈形成了。
  • 函數(shù)創(chuàng)建時,它的 prototype 屬性會拿到一個原型對象。當(dāng)函數(shù)作為構(gòu)造函數(shù),通過 new 創(chuàng)建一個新對象時,這個新對象的 [[prototype]] 會指向這個原型對象。
  • JS 要實現(xiàn) “類” 繼承,本質(zhì)是通過處理構(gòu)造函數(shù)的 prototype 對象來修改原型鏈。

以上就是一文詳解如何用原型鏈的方式實現(xiàn)JS繼承的詳細(xì)內(nèi)容,更多關(guān)于JS 原型鏈 繼承的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript分頁組件使用方法詳解

    JavaScript分頁組件使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了JavaScript分頁組件使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • JavaScript實現(xiàn)的一個計算數(shù)字步數(shù)的算法分享

    JavaScript實現(xiàn)的一個計算數(shù)字步數(shù)的算法分享

    這篇文章主要介紹了JavaScript實現(xiàn)的一個計算數(shù)字步數(shù)的算法分享,本文先是講解了算法描述與實現(xiàn)原理,然后給出實現(xiàn)代碼,需要的朋友可以參考下
    2014-12-12
  • js實現(xiàn)自動圖片輪播代碼

    js實現(xiàn)自動圖片輪播代碼

    這篇文章主要為大家詳細(xì)介紹了js實現(xiàn)自動輪播的實現(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • 基于JavaScript實現(xiàn)年份數(shù)字拼圖效果

    基于JavaScript實現(xiàn)年份數(shù)字拼圖效果

    時光荏苒,2022年又要收尾了,公司的年會是不是都安排上了?前幾天看到一個年會抽獎系統(tǒng),功能十分的強大,其中有一個年份數(shù)字的拼圖效果深深的吸引了哥,決定用JS實現(xiàn)一下該效果,需要的可以參考一下
    2022-12-12
  • Bootstrap fileinput組件封裝及使用詳解

    Bootstrap fileinput組件封裝及使用詳解

    這篇文章主要介紹了Bootstrap fileinput組件封裝及使用的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • JSON字符串操作移除空串更改key/value的介紹

    JSON字符串操作移除空串更改key/value的介紹

    今天小編就為大家分享一篇關(guān)于JSON字符串操作移除空串更改key/value的介紹,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • 分享Javascript中最常用的55個經(jīng)典小技巧

    分享Javascript中最常用的55個經(jīng)典小技巧

    這篇文章主要介紹了Javascript中最常用的55個經(jīng)典小技巧。需要的朋友可以過來參考下,希望對大家有所幫助
    2013-11-11
  • JavaScript判斷是否是微信瀏覽器

    JavaScript判斷是否是微信瀏覽器

    通過判斷UA中是否有關(guān)鍵字micromessenger,有的話則是微信內(nèi)置瀏覽器。下面小編給大家分享實現(xiàn)代碼,代碼簡單易懂,需要的朋友可以參考下
    2016-06-06
  • 不用AI也能實現(xiàn)的文字自動播報(SpeechSynthesis文本實例合成)

    不用AI也能實現(xiàn)的文字自動播報(SpeechSynthesis文本實例合成)

    SpeechSynthesis是HTML5的一個新特性,基于SpeechSynthesis可以實現(xiàn)在客戶瀏覽器端進(jìn)行動態(tài)文本的語音合成播放,這篇文章主要介紹了不用AI也能實現(xiàn)的文字自動播報(SpeechSynthesis文本實例合成),需要的朋友可以參考下
    2023-03-03
  • javascript支持IE和firefox(FF)的漸變透明效果

    javascript支持IE和firefox(FF)的漸變透明效果

    DataThis可以發(fā)送任何標(biāo)簽,這個標(biāo)簽沒有ID也可以,因為用的是自定義屬性。
    2008-10-10

最新評論