新蜂商城開源倉庫(內涵 Vue 2.x 和 Vue 3.x 的 H5 商城開源代碼,帶服務端 API 接口):github.com/newbee-ltdjavascript
Vue 3.x + Vant 3.x + Vue-Router 4.x 高仿微信記帳本開源地址(帶服務端 API 接口):github.com/Nick930826/…java
不要爲了面試而去背題,匆匆忙忙的,不只學不進去,背完了幾天後立刻會忘記。git
你可能會說,「沒辦法,這不是爲了能找份工做嘛!」。我想說的是,「那你沒開始找工做的時候,咋很差好學習呢。」github
好了,上述扯的這些,意思就是讓你們不要作收藏家,不要把好文收藏了,就放在收藏夾裏吃灰!面試
下面爲你們簡單闡述我對原型和原型鏈的理解,如果以爲有說的不對的地方,還望直接把頁面關閉了,別在我這篇文章上繼續浪費時間。(逃)微信
咱們先來了解下面引用類型的四個規則:markdown
一、引用類型,都具備對象特性,便可自由擴展屬性。函數
二、引用類型,都有一個隱式原型 __proto__
屬性,屬性值是一個普通的對象。oop
三、引用類型,隱式原型 __proto__
的屬性值指向它的構造函數的顯式原型 prototype
屬性值。學習
四、當你試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼它會去它的隱式原型 __proto__
(也就是它的構造函數的顯式原型 prototype
)中尋找。
引用類型:Object、Array、Function、Date、RegExp。這裏我姑且稱 proto 爲隱式原型,沒有官方中文叫法,你們都瞎叫居多。
下面咱們逐一驗證上面幾個規則,就會慢慢地理解原型和原型鏈。
引用類型,都具備對象特性,便可自由擴展屬性:
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
複製代碼
這個規則應該比較好理解,Date 和 RegExp 也同樣,就不贅述了。
引用類型,都有一個隱式原型 __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__);
複製代碼
引用類型,隱式原型 __proto__
的屬性值指向它的構造函數的顯式原型 prototype
屬性值:
const obj = {};
const arr = [];
const fn = function() {}
obj.__proto__ == Object.prototype // true
arr.__proto__ === Array.prototype // true
fn.__proto__ == Function.prototype // true
複製代碼
當你試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼它會去它的隱式原型 __proto__
(也就是它的構造函數的顯式原型 prototype
)中尋找:
const obj = { a:1 }
obj.toString
// ƒ toString() { [native code] }
複製代碼
首先, obj
對象並無 toString
屬性,之因此能獲取到 toString
屬性,是遵循了第四條規則,從它的構造函數 Object
的 prototype
裏去獲取。
我試圖想推翻上面的規則,看下面這段代碼:
function Person(name) {
this.name = name
return this // 其實這行能夠不寫,默認返回 this 對象
}
var nick = new Person("nick")
nick.toString
// ƒ toString() { [native code] }
複製代碼
按理說, nick
是 Person
構造函數生成的實例,而 Person
的 prototype
並無 toString
方法,那麼爲何, nick
能獲取到 toString
方法?
這裏就引出 原型鏈
的概念了, nick
實例先從自身出發反省本身,發現並無 toString
方法。找不到,就往上走,找 Person
構造函數的 prototype
屬性,仍是沒找到。構造函數的 prototype
也是一個對象嘛,那對象的構造函數是 Object
,因此就找到了 Object.prototype
下的 toString
方法。
上述尋找的過程就造成了原型鏈的概念,我理解的原型鏈就是這樣一個過程。也不知道哪一個人說過一句,JavaScript 裏萬物皆對象。從上述狀況看來,好像是這麼個理。🤔
用圖片描述原型鏈:
最後一個 null
,設計上是爲了不死循環而設置的, Object.prototype
的隱式原型指向 null
。
instanceof
運算符用於測試構造函數的 prototype
屬性是否出如今對象原型鏈中的任何位置。 instanceof
的簡易手寫版,以下所示:
// 變量R的原型 存在於 變量L的原型鏈上
function instance_of (L, R) {
// 驗證若是爲基本數據類型,就直接返回 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__; // 沒找到繼續向上一層原型鏈查找
}
}
複製代碼
咱們再來看下面這段代碼:
function Foo(name) {
this.name = name;
}
var f = new Foo('nick')
f instanceof Foo // true
f instanceof Object // true
複製代碼
上述代碼判斷流程大體以下:
一、 f instanceof Foo
: f
的隱式原型 __proto__
和 Foo.prototype
,是相等的,因此返回 true
。
二、 f instanceof Object
: f
的隱式原型 __proto__
,和 Object.prototype
不等,因此繼續往上走。 f
的隱式原型 __proto__
指向 Foo.prototype
,因此繼續用 Foo.prototype.__proto__
去對比 Object.prototype
,這會兒就相等了,由於 Foo.prototype
就是一個普通的對象。
再一次驗證萬物皆對象。。。。
經過四個特性、一個例子、一張圖片、一個方法,你們應該對原型和原型鏈的關係有了大概的認知。個人認知就是,原型鏈就是一個過程,原型是原型鏈這個過程當中的一個單位,貫穿整個原型鏈。就好像你要是看完了不點個贊,我能夠順着網線找到你。