前言
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 協議》,轉載必須註明作者和本文連結