面不面試的,你都得懂原型和原型鏈
作者:尼克陳
https://juejin.cn/post/6934498361475072014
前言
不要為了面試而去背題,匆匆忙忙的,不僅學不進去,背完了幾天后馬上會忘記。

你可能會說,“沒辦法,這不是為了能找份工作嘛!”。我想說的是,“那你沒開始找工作的時候,咋不好好學習呢。”
好了,上述扯的這些,意思就是讓大家不要做收藏家,不要把好文收藏了,就放在收藏夾里吃灰!
下面為大家簡單闡述我對原型和原型鏈的理解,若是覺得有說的不對的地方,還望直接把頁面關閉了,別在我這篇文章上繼續(xù)浪費時間。(逃)

四個規(guī)則
我們先來了解下面引用類型的四個規(guī)則:
1、引用類型,都具有對象特性,即可自由擴展屬性。
2、引用類型,都有一個隱式原型 __proto__ 屬性,屬性值是一個普通的對象。
3、引用類型,隱式原型 __proto__ 的屬性值指向它的構造函數(shù)的顯式原型 prototype 屬性值。
4、當你試圖得到一個對象的某個屬性時,如果這個對象本身沒有這個屬性,那么它會去它的隱式原型 __proto__(也就是它的構造函數(shù)的顯式原型 prototype)中尋找。
引用類型:Object、Array、Function、Date、RegExp。這里我姑且稱 proto 為隱式原型,沒有官方中文叫法,大家都瞎叫居多。
下面我們逐一驗證上面幾個規(guī)則,就會慢慢地理解原型和原型鏈。
規(guī)則一
引用類型,都具有對象特性,即可自由擴展屬性:
const obj = {}
const arr = []
const fn = function () {}
obj.a = 1
arr.a = 1
fn.a = 1
console.log(obj.a) // 1
console.log(arr.a) // 1
console.log(fn.a) // 1
復制代碼
這個規(guī)則應該比較好理解,Date 和 RegExp 也一樣,就不贅述了。
規(guī)則二
引用類型,都有一個隱式原型 __proto__ 屬性,屬性值是一個普通的對象:
const obj = {};
const arr = [];
const fn = function() {}
console.log('obj.__proto__', obj.__proto__);
console.log('arr.__proto__', arr.__proto__);
console.log('fn.__proto__', fn.__proto__);
復制代碼

規(guī)則三
引用類型,隱式原型 __proto__ 的屬性值指向它的構造函數(shù)的顯式原型 prototype 屬性值:
const obj = {};
const arr = [];
const fn = function() {}
obj.__proto__ == Object.prototype // true
arr.__proto__ === Array.prototype // true
fn.__proto__ == Function.prototype // true
復制代碼
規(guī)則四
當你試圖得到一個對象的某個屬性時,如果這個對象本身沒有這個屬性,那么它會去它的隱式原型 __proto__(也就是它的構造函數(shù)的顯式原型 prototype)中尋找:
const obj = { a:1 }
obj.toString
// ? toString() { [native code] }
復制代碼
首先, obj 對象并沒有 toString 屬性,之所以能獲取到 toString 屬性,是遵循了第四條規(guī)則,從它的構造函數(shù) Object 的 prototype 里去獲取。
一個特例
我試圖想推翻上面的規(guī)則,看下面這段代碼:
function Person(name) {
this.name = name
return this // 其實這行可以不寫,默認返回 this 對象
}
var nick = new Person("nick")
nick.toString
// ? toString() { [native code] }
復制代碼
按理說, nick 是 Person 構造函數(shù)生成的實例,而 Person 的 prototype 并沒有 toString 方法,那么為什么, nick 能獲取到 toString 方法?
這里就引出 原型鏈 的概念了, nick 實例先從自身出發(fā)檢討自己,發(fā)現(xiàn)并沒有 toString 方法。找不到,就往上走,找 Person 構造函數(shù)的 prototype 屬性,還是沒找到。構造函數(shù)的 prototype 也是一個對象嘛,那對象的構造函數(shù)是 Object ,所以就找到了 Object.prototype 下的 toString 方法。

上述尋找的過程就形成了原型鏈的概念,我理解的原型鏈就是這樣一個過程。也不知道哪個人說過一句,JavaScript 里萬物皆對象。從上述情況看來,好像是這么個理。??
一張圖片
用圖片描述原型鏈:

最后一個 null,設計上是為了避免死循環(huán)而設置的, Object.prototype 的隱式原型指向 null。
一個方法
instanceof 運算符用于測試構造函數(shù)的 prototype 屬性是否出現(xiàn)在對象原型鏈中的任何位置。instanceof 的簡易手寫版,如下所示:
// 變量R的原型 存在于 變量L的原型鏈上
function instance_of (L, R) {
// 驗證如果為基本數(shù)據(jù)類型,就直接返回 false
const baseType = ['string', 'number', 'boolean', 'undefined', 'symbol']
if(baseType.includes(typeof(L))) { return false }
let RP = R.prototype; // 取 R 的顯示原型
L = L.__proto__; // 取 L 的隱式原型
while (true) {
if (L === null) { // 找到最頂層
return false;
}
if (L === RP) { // 嚴格相等
return true;
}
L = L.__proto__; // 沒找到繼續(xù)向上一層原型鏈查找
}
}
復制代碼
我們再來看下面這段代碼:
function Foo(name) {
this.name = name;
}
var f = new Foo('nick')
f instanceof Foo // true
f instanceof Object // true
復制代碼
上述代碼判斷流程大致如下:
1、 f instanceof Foo:f 的隱式原型 __proto__ 和 Foo.prototype ,是相等的,所以返回 true 。
2、 f instanceof Object:f 的隱式原型 __proto__ ,和 Object.prototype 不等,所以繼續(xù)往上走。f 的隱式原型 __proto__ 指向 Foo.prototype ,所以繼續(xù)用 Foo.prototype.__proto__ 去對比 Object.prototype ,這會兒就相等了,因為 Foo.prototype 就是一個普通的對象。
再一次驗證萬物皆對象。。。。
總結
通過四個特性、一個例子、一張圖片、一個方法,大家應該對原型和原型鏈的關系有了大概的認知。我的認知就是,原型鏈就是一個過程,原型是原型鏈這個過程中的一個單位,貫穿整個原型鏈。就好像你要是看完了不點個贊,我可以順著網(wǎng)線找到你。
點贊和在看就是最大的支持??
