再解 JavaScript 原型與原型鏈

程式設計三昧發表於2022-02-02

原型與原型鏈.001

前言

JavaScript 原型與原型鏈雖然是一個老生常談的話題,但依然困惑著很多人,今天我再來從另一個角度談談這個問題。

兩個疑問

先看這樣一段程式碼:

let obj = {}
obj.__proto__.haha = 'gogo'
console.log(obj.haha) // "gogo"

執行一下上面的程式碼,輸出結果為 gogo

針對這個結果,有以下疑問:

  • obj 哪來的 __proto__屬性?
  • 為什麼新增到 __proto__上的屬性,可以直接透過 obj 拿到?

第一個問題

js 中每個物件都有一個“原型”,原型一般可以透過 __proto__訪問到:

let obj = {}
console.log(obj.__proto__)
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

可以這麼理解:原型,也是一個物件

就像:

  • 每個“人”都有一個“爸爸”;
  • 但“爸爸”也是一個“人”;
  • “爸爸”除了是某個人的爸爸外,與其他人並沒有本質的區別;
  • 爸爸,也是普通人。

類似的:

  • “原型”是一個普通的物件;
  • 爸爸也有他的爸爸,原型也有它的原型。

第二個問題

物件與人不同的是:

  • 人不可以隨便拿爸爸的東西
  • 而物件可以隨便拿原型裡的東西

比如,當你向一個物件,索要一個屬性時:

  • 如果這個物件沒有你要的屬性,它就會讓它的原型(爸爸)給你
  • 如果它爸也沒有,那它爸就會找它爸的爸

比如下面這個例子:

let obj = {
  __proto__: {
    __proto__: {
      haha: 'gogo'
    }
  }
}
console.log(obj.haha) // "gogo"

因為 obj 本身沒有 haha 這個屬性,所以它會去自己的 __proto__ 中查詢,如果還沒有找到,那就會向它的 __proto__.__proto__ 中去找,直到找到 haha 屬性或者 __proto__ 鏈返回 null 為止。

再換一種寫法:

let 爺爺 = {
  haha: 'gogo'
}
let 爸爸 = {
  __proto__: 爺爺
}
let obj = {
  __proto__: 爸爸
}
console.log(obj.haha) // "gogo"

查詢 haha 屬性的過程是: obj -> 爸爸 -> 爺爺 ,像不像是一條鏈子呢?這就是原型鏈。

有這樣一句話:

類是物件的模板

你與我,都是人,“人”是類,是模板。

你與我,都屬於“人”類,有很多共性:

  • 有一張嘴
  • 有兩條腿
  • 會吃飯
  • 會睡覺
    ……

這些共性是人類共有的。

當然,你與我做為獨立的物件,肯定也存在差異,比如:我的名字叫 X,你的名字叫 Y 。這些不同點,是物件“私有”的。

看一段 js 建立物件的程式碼(注意註釋部分):

function Person(name) {
  this.name = name
}
Person.prototype.吃飯 = function() {
  console.log('吃吃吃')
}
Person.prototype.睡覺 = function() {
  console.log('睡睡睡')
}

let= new Person('我').吃飯() // "吃吃吃"
console.log(Person.prototype ===.__proto__) // true
// 可以看出,在例項化的過程中,類的 prototype 成為物件的原型  

let= new Person('你').睡覺() // "睡睡睡"
console.log(.__proto__ ===.__proto__) // true
// 同一類的多個例項(物件),共享一個原型?.__proto__.吃飯 = function() {
  console.log('再吃一點')
}
console.log(.吃飯 ==.吃飯) // true.吃飯() // "再吃一點"
// 沒錯,同一類的多個例項,共享一個原型

類比於人類社會,就是:

  • 你的兄弟姐妹和你“共享”一個爸爸
  • 當你的爸爸燙了個頭時,你弟弟的爸爸也燙頭。這個過程中,不是兩個爸爸同時燙頭,而是本來就一個爸爸。

重要結論:

  • 例項化的過程中(也就是“當 new 一個物件的時候”),類的 prototype 成為物件的原型
  • 同一個類的多個例項(也就是“物件”),共享一個原型

結束語

原型是 js 底層的東西,不懂原型,幾乎不影響工作。

類似“原型有什麼用”的問題,就像“磚塊(或水泥)對蓋樓有什麼用”。其實在寫程式碼的過程中,幾乎不會用到原型的知識。但是如果遇到了問題、出現了 bug、效能最佳化時,底層的知識是肯定有大用途的。

~

~ 本文完,感謝閱讀!

~

學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!

大家好,我是〖程式設計三昧〗的作者 隱逸王,我的公眾號是『程式設計三昧』,歡迎關注,希望大家多多指教!

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章