JavaScript原型和原型鏈

最近做前端需求,發(fā)現(xiàn)JavaScript對類和對象的實現(xiàn)跟其他語言有很大不同,做了一些學(xué)習(xí)和了解。

JavaScript是一種基于原型的語言,它和基于類的語言有所不同。基于類的語言中對象是類的實例,類可以從另外一個類繼承。而JavaScript中,所有對象都是實例,繼承也是直接從其他對象繼承。

原型

JavaScript中的所有事物都是對象:字符串、數(shù)組、日期,函數(shù),正則表達(dá)式等。每一個對象都關(guān)聯(lián)一個原型對象,用proto屬性即可以訪問到,這就是對象的“原型”。
每個對象都從它的原型對象繼承屬性。也就是原型對象的屬性,通過該對象都可以直接訪問。

以O(shè)bject類型的對象為例,通過對象直接量{}和new Object()創(chuàng)建的對象都具有同一個原型,用proto屬性或通過Object.prototype都可以獲得原型對象的引用。這些對象從它的原型Object.prototype屬性繼承屬性和方法,這就是”原型繼承“。

大部分對象最終都從Object.prototype繼承,下圖列出這個原型對象內(nèi)的屬性。


看下面繼承的這段代碼,可以更容易理解。

let p = {a:'a1'};
let q = Object.create(p);
console.log('q.a=' + q.a);
q.a = 'aaa';
q.b = 'bbb';
console.log('q.a=' + q.a);

上面代碼創(chuàng)建p對象,創(chuàng)建一個指定原型為p的q對象。然后對屬性a和b分別賦值。打印兩個對象如下:



從打印出的結(jié)果得到如下幾點:

  1. q通過指定p為原型創(chuàng)建,q對象的__proto__屬性就是p對象,這就是原型繼承。
  2. q從p繼承了屬性,所以第三行訪問q.a的時候得到了p.a的值。因為取值時如果對象本身沒有這個屬性就會到他的原型上繼續(xù)查找,再到原型的原型上查找直到找到。
  3. 給q的a屬性賦值,在q對象上新增了a屬性,而未修改它原型上a屬性的值。因為賦值時原型上有這個屬性但不會去修改,而是給對象新增自由屬性來實現(xiàn),相當(dāng)于覆蓋原型鏈上相同名字的屬性。
  4. 給q的b屬性賦值,q創(chuàng)建時沒有b屬性所以新增b屬性。

接下來在最后增加一行代碼

p.a = 'a2';

再次打印兩個對象值



修改p對象的屬性,q的原型也被修改了。這是原型繼承的原因。原型對象的屬性改了,繼承它對象的原型屬性也修改了。

原型鏈

除Object.prototype外,幾乎所有的對象都有原型,他們的構(gòu)造函數(shù)都有一個繼承自O(shè)bject.prototype的原型。因此[]和new Array()創(chuàng)建的數(shù)組類型對象,繼承自Array.prototype,并且同時繼承自O(shè)bject.prototype。

其他類型比如函,日期,表達(dá)式這些JS內(nèi)置對象都是如此。這樣一系列鏈接的原型對象就是“原型鏈”。
把之前的代碼稍作修改,通過打印的對象觀察原型鏈。

let p = {
    a: 'a'
};
let q = Object.create(p);
q.b = 'b';
let o = Object.create(q);
o.c = 'c';

藍(lán)色區(qū)域是p對象,粉色區(qū)域是q對象,o對象從q繼承原型,q對象從p對象繼承原型,這樣形成了一系列的原型對象,這就是原型鏈。

所以在JavaScript這種基于原型的語言,通過原型對象來繼承屬性和方法。

在JavaScript中類的實現(xiàn)基于原型繼承機(jī)制。類的所有實例對象都是從同一原型對象上繼承屬性,因此原型對象是類的核心。

構(gòu)造函數(shù)
使用new調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新對象,構(gòu)造函數(shù)的prototype屬性被用作新對象的原型。這意味著同一個構(gòu)造函數(shù)創(chuàng)建的所有對象都繼承自一個相同的對象,因此他們都是同一個類的成員。

function Person(name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype = {
    constructor: Person,
    introduction: function() {
        console.log('My name is ' + this.name + ', I\'m ' + this.age + '.');
    }
}

let dav = new Person('David', 16);
dav.introduction();
  • 按照約定構(gòu)造函數(shù)首字母大寫。定義構(gòu)造函數(shù)就是定義類。
  • 構(gòu)造函數(shù)必須通過new關(guān)鍵字調(diào)用。在定義構(gòu)造函數(shù)前就已經(jīng)創(chuàng)建了新對象,通過this關(guān)鍵字可以獲取這個新對象。
  • 構(gòu)造函數(shù)只是初始化this。構(gòu)造函數(shù)甚至不必返回這個新創(chuàng)建的對象,構(gòu)造函數(shù)會自動創(chuàng)建對象,然后將構(gòu)造函數(shù)作為這個對象的方法來調(diào)用一次,最后返回這個新對象。
  • 構(gòu)造函數(shù)是類的外在表現(xiàn),構(gòu)造函數(shù)的名字通常用類名,可以使用instanceof來檢測對象是否屬于某個類。
  • constructor指代它的構(gòu)造函數(shù)。構(gòu)造函數(shù)是類的公共標(biāo)識,constructor屬性為對象提供了類。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在JavaScript中,原型鏈作為一個基礎(chǔ),老生長談,今天我們就來深入的解讀一下原型鏈。 本章主要講的是下面幾點...
    Devinnn閱讀 1,497評論 1 6
  • 人生總有一些不如意。也許你正懷才不遇,也許你正因缺錢而嘆息,也許你覺得受到了不公平的待遇,也許你的事業(yè)正遭遇危機(jī),...
    趙妍華太陽花閱讀 566評論 2 4
  • 看來要來點勵志的才行。不然,實在懶得不行,想玩手機(jī)、煲劇、躺著、吃、喝飲料、發(fā)脾氣……(題外話:這輸入法實習(xí)不行,...
    momokochan閱讀 209評論 0 2
  • 十一期間,我和媽媽出去旅游。首先坐了地鐵九號線,到了天津站,轉(zhuǎn)為二號線,之后轉(zhuǎn)乘高鐵去到山東青島。 到了青島,導(dǎo)游...
    大皓皓啊閱讀 147評論 0 0
  • 以前看過一部電影,叫做《陪安東尼度過漫長歲月》,男主安東尼陪著一直暗戀的女生到海里尋找一條鯨魚,想要收集它發(fā)出的聲...
    慢性患者閱讀 4,151評論 0 0

友情鏈接更多精彩內(nèi)容