prototype 是原型嗎?終結 JS 原型疑問

JS 的原型和原型鏈能夠說是最基礎的知識了,一塊兒來總結下web

有圖有真相

avatar
avatar

這張流傳久遠的圖其實很好的將原型與原型鏈的關係表達了出來,咱們去分析圖中的指向就能夠了編輯器

實例對象

  • 就是圖中最左側的一列,包括由構造函數 Foo 生成的 f一、 f2 和 Object 生成的 o一、o2

構造函數

  • 就是圖中中間的一列,Functions

原型對象

  • 就是圖中最右邊的一列,Prototypes

實例與構造函數

  • 實例對象由 new 關鍵字調用構造函數生成,則稱該對象是這個構造函數的一個實例。構造函數有一個 prototype 屬性,而這個就是該實例的原型,那麼實例有沒有什麼屬性指向它的原型呢,確定是有的,這就是所謂的隱式原型 __proto__,同時 ES5 也新增了 Object.getPrototypeOf() 這一靜態方法獲取一個對象的原型,代碼以下:
// Foo.prototype 是實例對象 f 的原型對象
function Foo(){} const f = new Foo() f.__proto__ === Foo.prototype // true Object.getPrototypeOf(f) === Foo.prototype // true 複製代碼

原型的原型對象

  • 上面咱們從實例出發,找到了它的原型指向的是構造函數的 prototype 屬性;但實例的原型也是一個對象,它應該也存在本身的原型,那麼這個原型對象的原型該怎麼找呢,其實和上面是同樣的,也是指向它構造函數的 prototype 屬性。這個原型對象的構造函數是 Object,所以:
f.__proto__.__proto__ === Object.prototype // true
const fOfPrototype = Object.getPrototypeOf(f); // 獲得實例的原型對象 const pOfPrototype = Object.getPrototypeOf(fOfPrototype); // 獲得原型對象的原型 pOfPrototype === Object.prototype // true 複製代碼

原型鏈的終點

  • 咱們經過一個普通實例對象,找到了它的原型對象,又找到了原型對象的原型,指向 Object.prototype,這同時說明 Object.prototype 也是一個對象,那麼按照上面的結論,它應該也有本身的原型,即 Object.prototype.__proto__,那麼這個對象是什麼呢,到了這裏,就走到了原型鏈的終點 null,即:
Object.prototype.__proto__ === null // true
Object.getPrototypeOf(Object.prototype) === null // true 複製代碼

函數對象的原型

  • 從一個普通實例沿着原型鏈一直找到終點 null,原型的知識就結束了嗎,固然沒有,對照開篇的圖能夠看到,函數被咱們漏掉啦,那麼接下來就分析一下函數的原型。其實很簡單,跟上面是同樣的。咱們知道在 JS 裏,函數也是一個對象,那麼它的原型固然也指向它構造函數的 prototype 屬性,而函數的構造函數是 Function,即:
const bar = new Function();
bar.__proto__ === Function.prototype //true Object.getPrototypeOf(bar) === Function.prototype // true  // 開篇使用的構造函數 Foo.__proto__ === Function.prototype //true Object.getPrototypeOf(Foo) === Function.prototype; //true 複製代碼
  • 函數的原型對象是 Function.prototype,一樣的問題,那這個原型對象的原型指向哪裏呢,其實上面已經分析過了,由於 Function.prototype 是一個對象,那麼它的原型也是指向其構造函數的 prototype 屬性,即:
Function.prototype.__proto__ === Object.prototype     //true
複製代碼
  • 到了 Object.prototype 這一層,再往下走就到達了原型鏈的終點

特殊對象的原型

  • Object 是生成對象的構造函數,是一個函數對象,所以它的原型是:
// 指向其構造函數的 prototype 屬性
Object.__proto__ === Function.prototype //true 複製代碼
  • Function 是生成函數的構造函數,是一個函數對象,所以它的原型是:
// 仍是指向其構造函數的 prototype 屬性
Function.__proto__ === Function.prototype //true 複製代碼

總結

  • 咱們已經把開篇圖中全部對象和函數間的指向分析完畢,其實 JS 原型相關的知識很簡單,總結起來就一句話: 對象的原型指向其構造函數的 prototype 屬性,原型又有它本身的原型,沿着原型鏈一直向上回溯,一直找到 Object.prototype,而它的原型是 null,到達原型鏈的終點
  • 只不過普通對象和函數對象稍有不一樣。咱們知道在 JS 中,構造函數其實就是一個普通函數,只是由於使用了 new 關鍵字去調用,才具備了構造函數的特性。所以幾乎全部的函數對象無論有沒有做爲構造函數去調用,除了具備指向其自身原型的 __proto__ 屬性外,還有一個 prototype 屬性,即:
bar.hasOwnProperty('prototype')         //true
複製代碼
  • 爲何說是幾乎全部的函數對象呢,由於確實有一種函數沒有這個屬性,即:
const bound = bar.bind(null);
bound.hasOwnProperty('prototype'); //false 複製代碼
  • 一樣的,幾乎全部對象都有繼承自原型的方法,以及指向其原型的 __proto__屬性,除了:
// obj 是一個真正的空對象,沒有任何原型上的方法,好比 toString() 等
const obj = Object.create(null); 複製代碼

測試

  • 咱們一般能夠使用 instanceof 運算符檢測一個對象是不是另外一個構造函數的實例,其原理就是 檢測運算符右側構造函數的 prototype 屬性值是否出如今運算符左側實例對象的原型鏈上。那麼運用如今學到的知識,結合開篇圖,看看下列代碼的輸出結果:
Function instanceof Object
Object instanceof Function Function.prototype instanceof Object Function.__proto__ instanceof Object 複製代碼
  • 輸出結果:
全是 true
複製代碼

本文使用 mdnice 排版函數

相關文章
相關標籤/搜索