JavaScript中Prototype、__proto__

「__proto__」

JavaScript是一個面向對象語音,即一切皆對象。app

那麼怎麼生成對象?在Java的世界裏,對象是由類(Class)實例出來的,通俗地說,就是將事物抽象成一個模具,用這個模具(類)生產出一個個具體的實物(對象)。函數

但是JS中沒有類這個概念,有的是「原型」,對象是由原型衍生出來的。通俗地說,在JS的世界裏,「原型」並非一個模具,而是一個具體的實物(對象)。全部對象都是由另外一個對象衍生出來的,而這個被衍生的對象就是所謂的「原型對象」。spa

每個對象自動生成一個屬性:__proto__,這個屬性是一個引用對象,其指向這個對象的「原型對象」。prototype

Talk is cheap, show me the code! 我們來看看代碼:設計

var obj = {};
console.log(obj);

clipboard.png

我們將__proto__展開看看:是一些默認方法。
clipboard.pngcode

你必定會發生這個__proto__對象中也有一個__proto__對象,正如咱們剛纔說的,每一個對象都有一個__proto__屬性指向它的原型對象。咱們打印一下這個__proto__中的__proto__:對象

console.log(obj.__proto__.__proto__); //--> null

結果是null,說明已經到了頂層原型對象。obj是用大括號{}定義的,obj的原型對象天然是JS的頂層對象。繼承

我們再看一端代碼,增強下理解:圖片

var parent = {
    name : "parent"
};
var child = {
    name : "child",
    __proto__ : parent
};
var subChild = {
    name : "subChild",
    __proto__ : child
}
console.log(subChild);

clipboard.png

subChild.__proto__ --> child
child.__proto__ --> parent
parent.__proto__ --> 頂層原型對象ip

prototype

上面咱們說到,每個對象都包含一個__proto__,指向這個的對象的「原型」。
相似的事情是,每個函數都包含一個prototype,這個prototype對象幹什麼的了?

我們看看以下代碼,用構造函數來建立一個對象(上面是用字面量的形式建立對象)。

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);

試想一想,這個foo對象的__proto__會指向什麼?
clipboard.png

一個包含constructor屬性的對象?看不太懂不要緊,把函數Foo的prototype屬性打印出來,對比一下就知道了。

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);
console.log(Foo.prototype);
console.log(foo.__proto__ === Foo.prototype);

clipboard.png

原來,new出來的對象foo的__proto__就只指向函數Foo的prototype。
foo.__proto__ --> Foo.prototype

JS這麼設計有何意義了?回憶下上面說的,在JS的世界中,對象不是根據類(模具)建立出來的,而是從原型(另外一個對象)衍生出來的。

當咱們執行new操做建立一個新的對象時,先不深刻new操做的具體實現,但有一點咱們是確定的——就是爲新對象的__proto__指向一個原型對象。

就剛纔這段代碼

function Foo(){};
var foo = new Foo();

foo.__proto__到底要指向誰了?你怎麼不能指向Foo這個函數自己吧,雖然函數也是對象,這個有機會會詳細講。但如何foo.__proto__指向Foo當然不合適,由於Foo是一個函數,有不少邏輯代碼,foo做爲一個對象,繼承邏輯處理沒有任何意義,它要繼承的是「原型對象」的屬性。

因此,每一個函數會自動生成一個prototype對象,由這個函數new出來的對象的__proto__就指向這個函數的prototype。
foo.__proto__ --> Foo.prototype

總結

說了這麼多,感受仍是沒徹底說清楚,不如上一張圖。我曾經參考過其餘網友的圖,但總以爲哪裏沒說清楚,因此我本身畫了一張圖,若是以爲個人不錯,請點個贊!(老子但是費了牛勁才畫出來)。
圖片描述
(點擊可放大)

我們就着這張圖,記住以下幾個事實:

1. 每一個對象中都有一個_proto_屬性。

JS世界中沒有類(模具)的概念,對象是從另外一個對象(原型)衍生出來的,因此每一個對象中會有一個_proto_屬性指向它的原型對象。(參考左上角的那個用字面量形式定義的對象obj,它在內存中開闢了一個空間存放對象自身的屬性,同時生成一個_proto_指向它的原型——頂層原型對象。)

2. 每一個函數都有一個prototype屬性。

「構造函數」爲什麼叫構造函數,由於它要構造對象。那麼根據上面第一條事實,構造出來的新對象的_proto_屬性指向誰了?總不能指向構造函數自身,雖然它也是個對象,但你不但願新對象繼承函數的屬性與方法吧。因此,在每一個構造函數都會有一個prototype屬性,指向一個對象做爲這個構造函數構造出來的新對象的原型。

3. 函數也是對象。

每一個函數都有一些通用的屬性和方法,好比apply()/call()等。但這些通用的方法是如何繼承的呢?函數又是怎麼建立出來的呢?試想一想,一切皆對象,包括函數也是對象,並且是經過構造函數構造出來的對象。那麼根據上面第二條事實,每一個函數也會有_proto_指向它的構造函數的prototype。而這個構造函數的函數就是Function,JS中的全部函數都是由Function構造出來的。函數的通用屬性與方法就存放在Function.prototype這個原型對象上。

相關文章
相關標籤/搜索