認識原型對象和原型鏈


在Javascript中,萬物皆對象,但對象也有區別,大致可以分為兩類,即:普通對象 Object 和 函數對象 Function

一般而言,通過 new Function 產生的對象是函數對象,其他對象都是普通對象。

舉例說明:

function f1() {
    // todo
}
var f2 = function () {
    // todo
};
var f3 = new Function('x',  'console.log(x)');

var o1 = {};
var o2 = new Object();
var o3 = new f1();

console.log(
    typeof f1,
    typeof f2,
    typeof f3,
    typeof o1,
    typeof o2,
    typeof o3
);
> function function function object object object

f1屬於函數的聲明,最常見的函數定義方式,f2實際上是一個匿名函數,把這個匿名函數賦值給了f2,屬於函數表達式,f3不常見,但也是一種函數對象。

Function 是JS自帶的對象,f1,f2 在創建的時候,JS會自動通過 new Function() 的方式來構建這些對象,因此,這三個對象都是通過 new Function() 創建的。

在 Javascript 中創建對象有兩種方式:對象字面量和使用new表達式,o1和o2的創建恰好對應了這兩種方式,重點講一下o3, 如果用Java和C#的思路來理解的話,o3是f1的實例對象,o3和f1是同一類型,至少我以前這么認為,其實不然...

怎么理解呢? 很簡單,看 o3 是不是通過 new Function 產生的,顯然不是,既然不是函數對象,那就是普通對象 。

通過對函數對象和普通對象的簡單理解之后,我們再來了解一下 Javascript 中的原型和原型鏈:

在 JS 中,每當創建一個函數對象 f1 時,該對象中都會內置一些屬性,其中包括 prototype 和 __proto__, prototype 即原型對象,它記錄着f1的一些屬性和方法。

需要注意的是,prototype 對 f1 是不可見的,也就是說,f1 不會查找 prototype 中的屬性和方法。 

function f(){}
f.prototype.foo = "abc";
console.log(f.foo); // undefined

那么,prototype 有什么用呢? 其實 prototype 的主要作用就是繼承。 通俗一點講,prototype 中定義的屬性和方法都是留給自己的 “后代” 用的,因此,子類完全可以訪問prototype中的屬性和方法。

想要知道 f1 是如何把 prototype 留給“后代”,我們需要了解一下 JS 中的原型鏈。此時,JS中的 __proto__ 入場了,它存在於普通對象和函數對象中,它的作用就是引用父類的 prototype 對象,JS在通過 new 操作符創建一個對象的時候,通常會把父類的 prototype 賦值給新對象的 __proto__屬性,這樣就形成了一代代傳承...

function f() {}
f.prototype.foo = "abc";
var obj = new f();
console.log(obj.foo); // abc

現在我們知道,obj中__proto__保存的是 f 的 prototype,那么 f 的 prototype 中的 __proto__ 中保存的是什么呢? 看下圖:

如圖所示,f.prototype 的 __proto__ 中保存的是 Object.prototype,Object.prototype 對象中也有 __proto__,而從輸出結果看,Object.prototype.__proto__ 是 null,表示 obj 對象原型鏈的終結。如下圖所示:

obj 對象擁有這樣一個原型鏈以后,當 obj.foo 執行時,obj 會先查找自身是否有該屬性,但不會查找自己的 prototype,當找不到foo時,obj 就沿着原型鏈依次去查找...

在上面的例子中,我們在f的 prototype 上定義了 foo 屬性,這時 obj 就會在原型鏈上找到這個屬性並執行。 

最后,總結一下本文中涉及到的重點:

  • 原型鏈的形成真正是靠__proto__ 而非prototype,當JS引擎執行對象的方法時,先查找對象本身是否存在該方法,如果不存在,會在原型鏈上查找,但不會查找自身的prototype。
  • 一個對象的 __proto__ 記錄着自己的原型鏈,決定了自身的數據類型,改變 __proto__ 就等於改變對象的數據類型。
  • 函數的 prototype 不屬於自身的原型鏈,它是創建子類的核心,決定了子類的數據類型,是連接子類原型鏈的橋梁。
  • 在原型對象上定義方法和屬性,是為了被子類繼承和使用。

 

原創發布  @一像素 2015.12

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM