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

圖解JS原型和原型鏈實(shí)現(xiàn)原理

 更新時(shí)間:2020年09月15日 10:03:44   作者:小幾斤  
這篇文章主要介紹了圖解JS原型和原型鏈實(shí)現(xiàn)原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

在我初學(xué) JS 語(yǔ)言的繼承機(jī)制原型和原型鏈的時(shí)候,我一直理解不了這種設(shè)計(jì)機(jī)制,再加上之前原有對(duì) Java繼承的理解,在學(xué)習(xí) JS 繼承機(jī)制的設(shè)計(jì)上踩了一個(gè)大坑,很多知識(shí)點(diǎn)前期都是死記硬背,無(wú)法真正的理解它的設(shè)計(jì)思想。

JS 中的繼承機(jī)制思想可以說(shuō)是學(xué)習(xí) JS 的一個(gè)核心思想,更可以說(shuō)是 JS 中的一個(gè)命脈,往往這些復(fù)雜、抽象的繼承關(guān)系,以及專(zhuān)業(yè)術(shù)語(yǔ)、代名詞確成為了困擾初學(xué)者的絆腳石。當(dāng)我真正理解它的設(shè)計(jì)思想時(shí),其實(shí)并沒(méi)有那么復(fù)雜,而且覺(jué)得非常簡(jiǎn)單。

在寫(xiě)這篇 JS 的原型和原型鏈的文章之前,我在谷歌搜索檢索了大量的高贊有關(guān) JS 原型和原型鏈的文章,大部分都是圍繞著“是什么”來(lái)講的,導(dǎo)致部分初學(xué)者缺少對(duì) JS 繼承的設(shè)計(jì)與實(shí)現(xiàn)的前后關(guān)聯(lián)性,還是很難準(zhǔn)確的去理解。

我們先要明白,學(xué)習(xí)這塊內(nèi)容知識(shí)要知道設(shè)計(jì)者“ 為什么這樣做 ” 遠(yuǎn)比 “怎么做的” 重要的多這才是掌握這部分內(nèi)容的關(guān)鍵。

今天小鹿對(duì) JS 的繼承機(jī)制要做一個(gè)系統(tǒng)的總結(jié),從設(shè)計(jì)者的角度出發(fā),將復(fù)雜的設(shè)計(jì)思想用動(dòng)畫(huà)呈現(xiàn),將零碎的知識(shí)點(diǎn)體系化,爭(zhēng)取讓你一文搞懂 JS 的繼承機(jī)制思想(原型和原型鏈)。

思維導(dǎo)圖

1.JS 的發(fā)展史

要想貫徹 JS 的核心設(shè)計(jì)思想,我們要從 JS 的誕生說(shuō)起。

1.1 為什么會(huì)誕生 JavaScript ?

相對(duì)比較成熟的瀏覽器是由網(wǎng)景公司發(fā)布的,早些年間,瀏覽器只能瀏覽網(wǎng)頁(yè)內(nèi)容,而不能進(jìn)行用戶(hù)交互。比如我們登錄輸入用戶(hù)名和密碼,在瀏覽器是不能進(jìn)行判斷用戶(hù)是否真正輸入了,而是通過(guò)服務(wù)器來(lái)判斷,如果沒(méi)有輸入,返回錯(cuò)誤提示用戶(hù),這種設(shè)計(jì)非常的浪費(fèi)時(shí)間和服務(wù)器資源。

為了解決這個(gè)問(wèn)題,網(wǎng)景公司需要開(kāi)發(fā)一種運(yùn)行在瀏覽器中的腳本語(yǔ)言,用來(lái)簡(jiǎn)單的做用戶(hù)輸入校驗(yàn)等操作。

當(dāng)時(shí)最流行的語(yǔ)言是面向?qū)ο蟮腏ava編程語(yǔ)言 ,網(wǎng)景公司為了能夠借助 Java將瀏覽器腳本語(yǔ)言流傳開(kāi),所以起名 JavaScript。其實(shí)兩者沒(méi)有任何的關(guān)系。

1.2 存在的問(wèn)題

JS 中的數(shù)據(jù)類(lèi)型設(shè)計(jì)受當(dāng)時(shí) Java流行的影響,都是對(duì)象類(lèi)型,這時(shí)候就遇到問(wèn)題了,有對(duì)象必然涉及到繼承機(jī)制,那么 JS 的繼承機(jī)制要設(shè)計(jì)成 Java一樣呢?還是另有設(shè)計(jì)思想?

2.JS 繼承的設(shè)計(jì)思想

JS 的開(kāi)發(fā)者想如果設(shè)計(jì)成像 Java一樣有“類(lèi)”的概念豈不是和 Java一樣成為了一種完全面向?qū)ο蟮木幊陶Z(yǔ)言了?最后決定自己設(shè)計(jì)一種繼承機(jī)制,但是它的設(shè)計(jì)思想還是采用了 Java的一些特性。

2.1 生成對(duì)象

通常 Java 生成對(duì)象是通過(guò) new 的方式,通過(guò)類(lèi)生成一個(gè)實(shí)例對(duì)象的過(guò)程。但是 JS 中并沒(méi)有類(lèi),那 JS 的設(shè)計(jì)者要怎么做?

他找到了 Java 和 JS 的共同點(diǎn)就是兩者都有構(gòu)造函數(shù), Java的 new 的過(guò)程內(nèi)部其實(shí)調(diào)用了構(gòu)造函數(shù)。但是 JS 是沒(méi)有“類(lèi)”的概念的,于是 JS 就把new 一個(gè)“類(lèi)”設(shè)計(jì)成了 new 一個(gè)構(gòu)造函數(shù),于是構(gòu)造函數(shù)成為了一個(gè)實(shí)例對(duì)象的原型對(duì)象。

3.為什么要設(shè)計(jì)原型對(duì)象?

上述這樣的原型設(shè)計(jì)有一個(gè)致命的缺點(diǎn)就是無(wú)法共享公共屬性。

因?yàn)槲覀冎?,?new 一個(gè)對(duì)象,生成的實(shí)例是兩個(gè)不同的對(duì)象。所以共有的屬性也不是共享的。

所以要設(shè)計(jì)一個(gè)對(duì)象專(zhuān)門(mén)用來(lái)存儲(chǔ)對(duì)象共享的屬性,那么我們叫它「原型對(duì)象」。

4.什么是原型對(duì)象?

要想讓構(gòu)造函數(shù)生成的所有實(shí)例對(duì)象都能夠共享屬性,那么我們就給構(gòu)造函數(shù)加一個(gè)屬性叫做prototype,用來(lái)指向原型對(duì)象,我們把所有實(shí)例對(duì)象共享的屬性和方法都放在這個(gè)構(gòu)造函數(shù)的prototype屬性指向的原型對(duì)象中,不需要共享的屬性和方法放在構(gòu)造函數(shù)中。

這里有一點(diǎn)疑惑就是,我們知道對(duì)象可以設(shè)置屬性,函數(shù)也可以設(shè)置屬性嗎?對(duì)于初學(xué)者來(lái)說(shuō)是比較懵逼的,那我們可以稍微的簡(jiǎn)單說(shuō)一下:

JavaScript 中的函數(shù)擁有對(duì)象的所有能力,也因此可被稱(chēng)作為任意其他類(lèi)型對(duì)象來(lái)對(duì)待。當(dāng)我們說(shuō)函數(shù)是第一類(lèi)對(duì)象的時(shí)候,就是說(shuō)函數(shù)也能夠?qū)ο蟮囊恍┕δ埽热缣砑訉傩?,函?shù)當(dāng)做參數(shù)傳遞等。

所以說(shuō),實(shí)例對(duì)象一旦通過(guò)構(gòu)造函數(shù)創(chuàng)建,就會(huì)自動(dòng)給實(shí)例對(duì)象賦值上原型對(duì)象上共享的屬性或方法。說(shuō)清楚一點(diǎn)就是該對(duì)象屬性都指向了原型對(duì)象的屬性值。

5.對(duì)象和函數(shù)在原型鏈關(guān)系?

上述的圖反映了對(duì)象以及函數(shù)在原型鏈中的關(guān)系,如果你覺(jué)的上邊的這張圖看懵逼了,沒(méi)關(guān)系,我剛開(kāi)始學(xué)習(xí)原型鏈的時(shí)候,根本不知道上邊這是什么“清明上河圖”,小鹿下面通過(guò)一步步的拆分講解,看這張圖就非常簡(jiǎn)單,沒(méi)錯(cuò),非常簡(jiǎn)單。

我們文章的開(kāi)頭也說(shuō)了什么是原型對(duì)象,說(shuō)白了就是構(gòu)造函數(shù)的一個(gè) prototype屬性,這個(gè)屬性就指向原型對(duì)象。

其實(shí)我們其中一些連接屬性沒(méi)有講到,只講到了prototype屬性,下面一張圖來(lái)將剩下的屬性補(bǔ)充完整,我們只要把這張圖印到大腦中就可以了。

我們來(lái)分析一下上圖,首先我們先要聲明一個(gè)狗的構(gòu)造函數(shù),定義其名字和體重屬性(私有屬性),同時(shí)每個(gè)構(gòu)造函數(shù)我們上邊講到了,都會(huì)有一個(gè)prototype屬性。

這個(gè)prototype指向的就是原型對(duì)象,原型對(duì)象放的就是對(duì)象共享的屬性。但是注意,原型對(duì)象里有一個(gè)constructor屬性,這個(gè)屬性又指回了構(gòu)造函數(shù)。

我們通過(guò) new 構(gòu)造函數(shù)生成兩個(gè)狗的對(duì)象實(shí)例,一個(gè)叫豆豆,一個(gè)叫貝貝,這兩個(gè)是兩個(gè)不同的對(duì)象,名字體重都不相同,但是他們會(huì)共享原型對(duì)象上的屬性 type,它們共有的屬性都是犬類(lèi)。

在 JS 所有對(duì)象中,只要是對(duì)象,都會(huì)有一個(gè)內(nèi)置屬性叫做_proto_,而且這個(gè)屬性是系統(tǒng)自動(dòng)生成的,只要你創(chuàng)建一個(gè)對(duì)象,這個(gè)對(duì)象就有這個(gè)屬性。這個(gè)_proto_屬性指向的是原型對(duì)象。

通過(guò)上邊的分布講解,我們明白了構(gòu)造函數(shù)與對(duì)象實(shí)例以及原型對(duì)象的關(guān)系。

總結(jié)為一句話為:

構(gòu)造函數(shù)的 prototype 指向原型對(duì)象,原型對(duì)象有一個(gè) constructor 屬性指回構(gòu)造函數(shù),每個(gè)構(gòu)造函數(shù)生成的實(shí)例對(duì)象都有一個(gè) _proto_ 屬性,這個(gè)屬性指向原型對(duì)象。

沒(méi)錯(cuò),原型就是這么簡(jiǎn)單。但是你會(huì)發(fā)現(xiàn),原型也是對(duì)象呀,你說(shuō)只要是對(duì)象都會(huì)有一個(gè)_proto_屬性指向自身構(gòu)造函數(shù)的原型對(duì)象。

沒(méi)錯(cuò),要想知道原型對(duì)象的_proto_屬性指向誰(shuí),就要知道是哪個(gè)構(gòu)造函數(shù)創(chuàng)建了原型對(duì)象?

我們知道,所有的 JS 對(duì)象的都是繼承了一個(gè)叫做 Object 的對(duì)象??梢岳斫鉃镺bject 構(gòu)造函數(shù)創(chuàng)造了這個(gè)萬(wàn)物,他們的關(guān)系如下,和上邊是同樣的道理,上邊總結(jié)的那句話好好理解一下。

但是上圖中會(huì)有一個(gè)疑問(wèn),Object 構(gòu)造函數(shù)原型對(duì)象的也是對(duì)象,它肯定也有一個(gè)_proto_屬性,為什么會(huì)指向 null 呢?

我們?cè)谀蒙鲜隹偨Y(jié)的那句話,_proto_屬性指向的是自身構(gòu)造函數(shù)的原型對(duì)象,自身的構(gòu)造函數(shù)是誰(shuí)?是 Object 構(gòu)造函數(shù),那 Object構(gòu)造函數(shù)的原型是誰(shuí)?當(dāng)然是本身(如圖),所以把_proto_指向了null。

上邊的關(guān)系如果不仔細(xì)整理的話確實(shí)很亂,尤其是對(duì)于初學(xué)者,但是如果像小鹿這樣已整理,再亂的關(guān)系把它安排的井井有條,沒(méi)有理解,就多看幾篇文章。

6.原型鏈

我們還有一個(gè)問(wèn)題沒(méi)有解決就是原型鏈?既然我么你知道什么是原型了,原型鏈?zhǔn)鞘裁?#63;顧名思義,肯定是一條鏈,既然每個(gè)對(duì)象都有一個(gè)_proto_屬性指向原型對(duì)象,那么原型對(duì)象也有_proto_指向原型對(duì)象的原型對(duì)象,直到指向上圖中的null,這才到達(dá)原型鏈的頂端。

不要忘了,上邊那種圖我們還沒(méi)有把它理解,我們把圖自上而下理解。

第一張圖分解,上邊小鹿畫(huà)的圖的關(guān)系和這個(gè)一樣的,仔細(xì)對(duì)比一下,很簡(jiǎn)單,第一張圖就這么解決了。

我們繼續(xù)向下分割,看第二張圖。

第二張圖怎么還是那么眼熟呢,這不是小鹿上邊分析的 Object 的關(guān)系圖嗎?對(duì)的,沒(méi)錯(cuò)。

第三張圖,稍微繞個(gè)彎子,但是換湯不換藥呀,聽(tīng)小鹿分析來(lái)。

看著還是眼熟,只不過(guò)把function換成了Function,f 變成了大寫(xiě)的 F,這里涉及到一個(gè)知識(shí)點(diǎn)就是,在 JS 中,所有的 function函數(shù)都是由Function繼承來(lái)的,可以說(shuō)是Function是所有 function的祖宗。

那Function是由誰(shuí)生產(chǎn)來(lái)的?我們看到圖中的Function函數(shù)有_proto_屬性了,而且屬性指向自己的原型對(duì)象,那不就是自己繁衍自己?jiǎn)?#63;可以這么理解。

小結(jié)

這里我們?cè)诳v觀全圖,總結(jié)幾條定義你比對(duì)著圖去找。

1、所有的實(shí)例的_proto_都指向該構(gòu)造函數(shù)的原型對(duì)象(prototype)。

2、所有的函數(shù)(包括構(gòu)造函數(shù))是Function的實(shí)例,所以所有函數(shù)的 _proto_的都指向Function的原型對(duì)象。

3、所有的原型對(duì)象(包括 Function的原型對(duì)象)都是Object的實(shí)例,所以_proto_都指向 Object(構(gòu)造函數(shù))的原型對(duì)象。而 Object構(gòu)造函數(shù)的 _proto_指向 null。

4、Function構(gòu)造函數(shù)本身就是 Function 的實(shí)例,所以_proto_指向Function的原型對(duì)象。

全篇文章的精華都在最后的總結(jié)部分,前邊的所有分解講解是為了讓你理解這些函數(shù)對(duì)象以及原型對(duì)象之間的關(guān)系,這關(guān)系都是固定的,誰(shuí)指向誰(shuí),都是寫(xiě)死額,只要你記住了他們的關(guān)系,這張圖就理解的差不多了,能夠理解完這張圖,你的原型和原型鏈已經(jīng)了解的很扎實(shí)了,但是還需要做一些面試題鞏固一下。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論