JS 中 __proto__ 和 prototype 存在的意義是什麼?

飢人谷前端發表於2018-02-10

本文為飢人谷講師方方原創文章。

JS 中 __proto__ 和 prototype 存在的意義是什麼?

  1. 你的 JS 程式碼還沒執行的時候,JS 環境裡已經有一個 window 物件了
  2. window 物件有一個 Object 屬性,window.Object 是一個函式物件
  3. window.Object 這個函式物件有一個重要屬性是 prototype,幹什麼用的等會說
  4. window.Object.prototype 裡面有這麼幾個屬性 toString(函式)、valueOf(函式) 好,目前先知道這些就夠了。 然後我們寫一句程式碼
var obj = {}
obj.toString()
複製程式碼

這句程式碼做了啥?為什麼 obj 有 toString() 屬性?

JS 中 __proto__ 和 prototype 存在的意義是什麼?

這句話大概是讓 obj 變數指向一個空物件,這個空物件有個 proto 屬性指向 window.Object.prototype。

這樣你在呼叫 obj.toString() 的時候,obj 本身沒有 toString,就去 obj.proro 上面去找 toString。

所以你呼叫 obj.toString 的時候,實際上呼叫的是 window.Object.prototype.toString

那麼 window.Object.prototype.toString 是怎麼獲取 obj 的內容的呢?

那是因為 obj.toString() 等價於 obj.toString.call(obj)

同時 obj.toString.call(obj) 等價於 window.Object.prototype.toString.call(obj)

這句話把 obj 傳給 toString 了。

再看複雜一點的

回到第一幅圖

JS 中 __proto__ 和 prototype 存在的意義是什麼?
我們寫一句程式碼

var arr = []
arr.push(1) // [1]
複製程式碼

請問這兩句話做了什麼?

JS 中 __proto__ 和 prototype 存在的意義是什麼?

看紅色部分,var arr = [] 大概會讓 arr 指向一個空物件,然後 arr.proto 指向 window.Array.prototype。(其實 arr 有一個 length:0,不過這裡就忽略吧)

這樣你在呼叫 arr.push 的時候,arr 自身沒有 push 屬性,就去 arr.proto 上找 push

因此 arr.push 實際上是 window.Array.prototype.push

arr.push(1) 等價與 arr.push.call(arr,1)

arr.push.call(arr,1) 等價於 window.Array.prototype.push.call(arr, 1)

再再複雜一點

arr.valueOf() 做了什麼?

arr 自身沒有 valueOf,於是去 arr.proto 上找

arr.proto 只有 pop、push 也沒有 valueOf,於是去 arr.proto.proto 上找

arr.proto.proto 就是 window.Object.prototype

所以 arr.valueOf 其實就是 window.Object.prototype.valueOf

arr.valueOf() 等價於 arr.valueOf.call(arr)

arr.valueOf.call(arr) 等價於 window.Object.prototype.valueOf.call(arr)

看,JavaScript 其實很優美很簡單。

只是你想複雜了而已:

prototype 指向一塊記憶體,這個記憶體裡面有共用屬性

proto 指向同一塊記憶體

prototype 和 proto 的不同點在於

prototype 是建構函式的屬性,而 proto 是物件的屬性

難點在於……建構函式也是物件!

如果沒有 prototype,那麼共用屬性就沒有立足之地

如果沒有 proto,那麼一個物件就不知道自己的共用屬性有哪些。

反證法

假設我們把 proto 去掉,那麼

var obj = {}
obj.toString() // 報錯,沒有 toString 方法
複製程式碼

所以你只能這樣宣告一個物件咯:

var obj = {
  toString: window.Object.prototype.toString,
  valueOf: window.Object.ptototype.valueOf
}
obj.toString() // '[object Object]'
複製程式碼

知道 proto 幫你省多少程式碼了嗎?

假設我們刪掉 prototype,包括 window.Object.prototype 和 window.Array.prototype。

那麼 window.Object.prototype.toString 也一併被刪除了。

然後我們基本就沒法寫程式碼了……

var obj = {}
obj.toString() // toString 不存在,因為 toString 沒有定義過啊
複製程式碼

prototype 的意義就是把共有屬性預先定義好,給之後的物件用。

自己想想吧~

新人搞不懂原型大抵是因為

  1. 不懂記憶體、引用
  2. 不懂連結串列、樹等資料結構
  3. 不知道函式是一種物件
  4. 被 Java 的 class 關鍵字毒害了
  5. 還有一種可能是因為沒遇到我方應杭:

「每日一題」什麼是 JS 原型鏈? JS 的 new 到底是幹什麼的? this 的值到底是什麼?一次說清楚

以上是「方三篇」新人一定要看哦。

想看視訊版本可以購買我的網課(收費): JS 深入淺出 - 寫程式碼啦!

這幅圖我還可以繼續講,把 JS 所有基礎知識都能串起來。

比如很多人不懂什麼是偽陣列,很簡單:

  1. 如果一個陣列的 proto 直接或間接指向 Array.prototye(用到了陣列的共用屬性),那麼就是真陣列
  2. 如果一個陣列的 proto 沒有直接或間接指向 Array.prototye,那麼就是偽陣列
var realArr = {0: 'a', 1:'b', length: 2}
realArr.__proto__ = Array.prototye
// 這就是真陣列
// 等價於 realArr = ['a', 'b']
realArr.push !== undefined // true

var fakeArr = {0: 'a', 1:'b', length: 2}
// 這就是偽陣列
realArr.push === undefined // true
複製程式碼

完。

加微訊號: astak10或者長按識別下方二維碼進入前端技術交流群 ,暗號:寫程式碼啦

每日一題,每週資源推薦,精彩部落格推薦,工作、筆試、面試經驗交流解答,免費直播課,群友輕分享... ,數不盡的福利免費送

JS 中 __proto__ 和 prototype 存在的意義是什麼?

相關文章