一 前言
在學JS過程中,之前一直對函式和物件的關係有疑惑。一方面,在JS中 函式也是物件;另一方面,物件卻又是通過建構函式實現的例項
那麼他們的關係到底是什麼?
PS: 關於物件是什麼,以及物件是 通過建構函式實現的,可參考:
二 前置知識和規則
在具體介紹之前,先介紹一下前置知識,這是理解下文的基礎
S1 物件
:
A1 物件是 一組key-value 資料(即屬性) 的集合;
方法本質上是 屬性值是函式的 特殊屬性;
S2 每個物件A 都有一個__proto__屬性
A1 __proto__屬性的特點:
指向另一個物件B, 我們可以極不準確的認為,B是A的 “父物件”;
A2 __proto__屬性的作用:
通過它,物件A除了可以訪問自身定義的屬性值(包括特殊屬性—方法),還可以訪問B物件的屬性;
S3 函式
:
A1 函式是 一個有特殊屬性(prototype屬性) 的特殊物件,
換言之,函式也是物件,可以具有屬性,它同時具有__proto__屬性和prototype屬性;
函式物件的最大作用是: 它具有獨立作用域 + 可以獨立封裝/執行一段程式碼片段,具有複用性;
A2 prototype屬性的特點: 函式物件F的prototype屬性,必然指向一個叫做 F.prototype的物件:
如果這個F.prototype物件 已存在, 那就直接指向它;
如果這個F.prototype物件 還不存在,那就立刻在函式定義執行後生成它;
A3 prototype屬性的作用:
當函式被作為特殊的建構函式使用時,可以讓新建立出來的 例項物件可以繼承 F.prototype物件的屬性,
換言之,
它的作用就是 引導 例項物件的__proto__屬性,使其指向F.prototype物件,從而構成原型鏈
PS: 關於prototype屬性,可參考
三 (構造)函式和物件 的過程詳解
S1
A1 首先,瀏覽器內建了一個物件,叫做Object.prototype
, 它是一個物件 + 物件都有__proto__屬性
ES規定了: Object.prototype.__proto = null
;
A2 同時,Object.prototype物件 預先規定了 自己的一些屬性和方法 (如 toSting方法);
S2
A1 接著,瀏覽器又實現了另一個物件,叫做Function.prototype
,它也是一個物件 + 物件都有__proto__屬性
ES又規定了: Function.prototype.__proto = Object.prototype
也就是說,我們可以簡單理解為 Object.prototype物件 是 Function.prototype物件 的父物件
;
A2 同時,Function.prototype物件
也預先規定了 自己的一些屬性和方法 (如 apply/call方法);
S3
A1 再後,瀏覽器又又實現了 另2個比較特殊的物件,分別叫做Function函式物件
和 Object函式物件
根據博文第二部分內容可知, 這2個都有一個獨有的屬性, 就是 prototype屬性
所以, Function 函式(物件) / Object函式(物件) 就同時有這兩個屬性 proto + prototype ;
A2 因為 函式物件F的prototype屬性,必然指向一個叫做 F.prototype的物件,所以:
Function.prototype值 = Function.prototype物件
(即 S2步驟 瀏覽器實現的內建物件),
Object.prototype值 = Object.prototype物件
(即 S1步驟 瀏覽器實現的內建物件);
A3 同時,ES規定:
Function.__proto__值 = Function.prototype物件, Object.__proto__值 = Function.prototype物件,
也就是說,可以理解為: Function.prototype物件 同時是 Object函式物件 和 Function函式物件 的父物件
;
S4
A1 有了 Object函式物件 和 Function函式物件,我們就可以構造出自己的 object例項物件 和 function例項物件了;
A2 例項物件的__proto__屬性值 指向的是 建構函式物件的prototype的 值物件
,
也就是說,例項物件和 (構造)函式物件的 原型物件
之間, 通過 __proto__屬性和prototype屬性,建立了聯絡
這就是原型鏈
S5
A1 同理, Array等函式(物件),也都有 __proto__屬性 和 prototype屬性,且 __proto__值 = Function.prototype物件
也就是說,我們可以簡單理解為 Function.prototype物件 還構造出了 Array等其他函式物件
綜合上面所有內容,一張經典圖理解為:
小結一下就是:
- 所有物件(包括函式)都有 __proto__原型屬性;
- 只有 (構造)函式 才有prototype屬性;
- 建構函式的 __proto__原型屬性 與 它的prototype屬性 不是一回事
前者是它作為物件時的屬性,表示這個函式是誰生的, 或者說是在誰的基礎上擴充套件來的
後者是它作為一個建構函式時特有的屬性,即用它來new一個物件時,這個被new出來的物件是用誰做模版的;
PS: 關於函式和物件的 關係,可參考
03 從__proto__和prototype來深入理解JS物件和原型鏈;