動態

詳情 返回 返回

再解 JavaScript 原型與原型鏈 - 動態 詳情

原型與原型鏈.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 -> 爸爸 -> 爺爺 ,像不像是一條鏈子呢?這就是原型鏈。

關於 prototype

有這樣一句話:

類是對象的模板

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

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

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

這些共性是人類共有的。

當然,你與我做為獨立的對象,肯定也存在差異,比如:我的名字叫 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、性能優化時,底層的知識是肯定有大用途的。

~

~ 本文完,感謝閲讀!

~

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

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

user avatar haoqidewukong 頭像 zaotalk 頭像 freeman_tian 頭像 kobe_fans_zxc 頭像 longlong688 頭像 Dream-new 頭像 zero_dev 頭像 solvep 頭像 dunizb 頭像 Z-HarOld 頭像 wmbuke 頭像 libubai 頭像
點贊 41 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.