從__proto__和prototype詳解物件和函式

ygming發表於2019-02-09

一 前言

在學JS過程中,之前一直對函式和物件的關係有疑惑。一方面,在JS中 函式也是物件;另一方面,物件卻又是通過建構函式實現的例項
那麼他們的關係到底是什麼?

PS: 關於物件是什麼,以及物件是 通過建構函式實現的,可參考:

01 深入理解javascript原型和閉包(1-2)

二 前置知識和規則

在具體介紹之前,先介紹一下前置知識,這是理解下文的基礎

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屬性,可參考

01 深入理解javascript原型和閉包(3)

02 JS中先有Object還是先有Function

三 (構造)函式和物件 的過程詳解

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等其他函式物件

綜合上面所有內容,一張經典圖理解為:

PSHSEj.jpg

小結一下就是:

  1. 所有物件(包括函式)都有 __proto__原型屬性;
  2. 只有 (構造)函式 才有prototype屬性;
  3. 建構函式的 __proto__原型屬性 與 它的prototype屬性 不是一回事
    前者是它作為物件時的屬性,表示這個函式是誰生的, 或者說是在誰的基礎上擴充套件來的
    後者是它作為一個建構函式時特有的屬性,即用它來new一個物件時,這個被new出來的物件是用誰做模版的;

PS: 關於函式和物件的 關係,可參考

01 深入理解javascript原型和閉包(4-7)

03 從__proto__和prototype來深入理解JS物件和原型鏈;

04 JavaScript深入之從原型到原型鏈

四 參考文件

  01深入理解javascript原型和閉包(1-7)

  02 JS中先有Object還是先有Function;

  03 深入理解JS物件和原型鏈;

  04 JavaScript深入之從原型到原型鏈

相關文章