Javavscript基礎——原型和原型鏈

本文研究一下Javascript的核心基礎——原型鏈和繼承。javascript

對於使用過基於類的語言(如Java或C#)的人來講,Javascript的繼承有點難以搞懂,由於它自己沒有class這種東西。(ES6中引入了class關鍵字,看上去也像傳統的OOP語言,可是那只是語法糖,底層仍是基於原型)。java

原型鏈

MDN上對於原型鏈的解釋:數組

當談到繼承時,JavaScript 只有一種結構:對象。每一個實例對象( object )都有一個私有屬性(稱之爲 __proto__ )指向它的構造函數的原型對象( prototype )。該原型對象也有一個本身的原型對象( __proto__ ) ,層層向上直到一個對象的原型對象爲 null。根據定義, null 沒有原型,並做爲這個 原型鏈中的最後一個環節。

幾乎全部 JavaScript 中的對象都是位於原型鏈頂端的 Object 的實例。網絡

這段話可能難以理解,咱們來舉個例子:數據結構

const list = []; // 定義數組
list.__proto__ === Array.prototype; // true
list.__proto__.__proto__ === Object.prototype; // true
list.__proto__.__proto__.__proto__===null; // true
// 繼承關係爲
// list -> Array.prototype -> Object.prototype -> null

結合MDN的解釋,咱們來解釋一下上述例子:函數

list是Array的實例對象,使用了字面量的方式建立了對象實例this

每一個實例對象( object )都有一個私有屬性(稱之爲 __proto__ )指向它的構造函數的原型對象( prototype )。
// list的構造函數是Array,因此list.__proto__指向構造函數Array的原型對象。
list.__proto__ === Array.prototype; // true
該原型對象也有一個本身的原型對象( __proto__ )
// Array.prototype也是對象,也有本身的原型對象,原型是Object.prototype
// 下面是數學運算(等量代換)
// list.__proto__ = Array.prototype
// Array.prototype.__proto__ = Object.prototype
list.__proto__.__proto__ === Object.prototype; // true
層層向上直到一個對象的原型對象爲 null。根據定義, null 沒有原型,並做爲這個 原型鏈中的最後一個環節。
// 目前咱們來到了Object.prototype,根據規範,Object.prototype的原型對象爲null
// list.__proto__ = Array.prototype
// Array.prototype.__proto__ = Object.prototype
// Object.prototype.__proto__ = null;
list.__proto__.__proto__.__proto__ === null; // true

原型鏈查找

當咱們訪問對象的屬性或者方法時,會先從對象自己開始查找,若是查找不到,則查找對象的 __proto__,層層向上查找,直到查找到屬性,不然拋出錯誤。
const list = [];
list.toString();

屬性查找過程以下:spa

  1. 查找list.toString()方法,沒找到
  2. 繼續查找list.__proto__,也就是Array.prototype,找到了
  3. 調用Array.prototype.toString

原型鏈結論

  1. 對象實例.__proto__ = 對象構造函數.prototype
  2. 幾乎全部對象的原型都是Object.prototype
  3. null是對象,可是null沒有原型
  4. 屬性/方法查找採用優先返回機制。

函數

通過原型鏈的簡單介紹,相信你們對原型和原型鏈有了一個比較直觀的瞭解了,如今要說到的是函數。prototype

咱們知道,Javascript中函數也是對象,因此 Function.__proto__指向 Object.prototype

上面的結論在Javascript中是有問題的。咱們來聊一聊函數。code

先看看簡單一點的例子,你們知道,Object是對象的構造函數構造函數也是函數,全部的函數的原型都是Function.prototype,因此Object.__proto__是等於Function.prototype的。

事實證實,也是如此。

image-20190923170248951

那麼Function.__proto__爲何不等於Object.prototype呢?Function不是對象嗎?

Function確實是對象,同時仍是構造函數,能夠經過new Function()來獲得函數實例。

上面咱們說到全部函數的原型是Function.prototype,因此Function這個構造函數的原型__proto__等於Function.prototype

基於以上原理,還有如下相等關係:

  • Object.__proto__ === Function.prototype
  • Array.__proto__ === Function.prototype

引伸的問題

咱們知道Function.__proto__是指向Function.prototype,那個Function.prototype這個Function哪裏來的?Function本身創造本身?那不是會死循環嗎?

這個問題不是純JS層面能解決的,牽涉到底層實現,下面是網絡上別人整理的結論,有須要的能夠研究一下V8的源碼,這樣能夠完全解決這個問題。

  1. 用C/C++ 構造內部數據結構建立一個 OP 即(Object.prototype)以及初始化其內部屬性但不包括行爲。
  2. 用 C/C++ 構造內部數據結構建立一個 FP 即(Function.prototype)以及初始化其內部屬性但不包括行爲。
  3. 將 FP 的[[Prototype]]指向 OP。
  4. 用 C/C++ 構造內部數據結構建立各類內置引用類型。
  5. 將各內置引用類型的[[Prototype]]指向 FP。
  6. 將 Function 的 prototype 指向 FP。
  7. 將 Object 的 prototype 指向 OP。
  8. 用 Function 實例化出 OP,FP,以及 Object 的行爲並掛載。
  9. 用 Object 實例化出除 Object 以及 Function 的其餘內置引用類型的 prototype 屬性對象。
  10. 用 Function 實例化出除Object 以及 Function 的其餘內置引用類型的 prototype 屬性對象的行爲並掛載。
  11. 實例化內置對象 Math 以及 Grobal
  12. 至此,全部 內置類型構建完成。

函數結論

  1. 函數的原型都是Function.protype,構造函數也是函數,因此構造函數的原型也是Function.prototype

來自靈魂的拷問1

下面是一道有點難度的JS基礎題,能夠感覺一下:

function A() {
  
}

function B(a) {
  this.a = a;
}

function C(a) {
  if(a) {
    this.a = a;
  }
}

A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);
console.log(new B().a);
console.log(new C().a);

輸出是

1
undefined
1

解釋

  1. 爲何輸出1?

    由於new A()這個對象上沒有屬性a,因此去查找原型鏈,查到了F.prototype.a
  2. 爲何輸出undefined?

    由於new B時沒有傳遞a,因此a是undefined,new B()這個對象是有a屬性的,只不過值是undefined,因此不查原型鏈
  3. 爲何輸出1?

    由於new C()未傳遞a,因此a是undefined,因爲if(a)的判斷,new C()這個對象內部沒有a屬性,因此去查原型鏈

來自靈魂的拷問2

function F() {
  this.a = 1;
}
F.prototype.b = 2;

var f = new F();
console.log(f.hasOwnProperty('a'));
console.log(f.hasOwnProperty('b'));

輸出是

true
false

解釋

  1. 爲何輸出true`?
輸出true比較好理解,由於構造函數 F聲明瞭屬性 a,因此 F的實例有 a屬性
  1. 爲何輸出false?
b是 f的原型對象 F.prototype的屬性,不是 b本身的,不能拿別人的說成本身的。

結尾

本文研究了原型和原型鏈之間的關係以及常見對象的原型和原型鏈,對於特殊對象Function也研究了一下,若是能搞懂後面兩個問題,那本文對你來講沒什麼問題了。

相關文章
相關標籤/搜索