__proto__和prototype之區別和聯繫

學到原型的時候感受頭都大了/(ㄒoㄒ)/~~ 尤爲是prototype和__proto__ 傻傻分不清,經過多番查找資料,根據本身的理解,記錄下最近研究對象的一些心得,作一個記錄與總結,以加深本身的印象,同時,但願也能給其餘學JavaScript同胞一點啓發。瀏覽器

在JavaScript中,萬物皆對象。我們寫一個JavaScript對象,大多數時候是用構造函數建立一個對象或者用對象字面量建立一個對象。好比:函數

//經過構造函數來建立對象
function Person() {
    //...
}
var person1 = new Person();
//經過對象字面量建立對象
var person2 = {
    name: 'jessica',
    age: 27,
    job: 'teacher'
}

固然還有其餘方式建立對象,這裏就不列舉出來了。那麼問題來了,經過不一樣的方式建立的對象有什麼區別呢?
咱們知道,每一個JS對象必定對應一個原型對象,並從原型對象繼承屬性和方法。那麼對象是怎麼和這個原型對象對應的呢?帶着問題慢慢看下面的內容吧this

__proto__和prototype概念區分

其實說__proto__並不許確,確切的說是對象的[[prototype]]屬性,只不過在主流的瀏覽器中,都用__proto__來表明[[prototype]]屬性,由於[[prototype]]只是一個標準,而針對這個標準,不一樣的瀏覽器有不一樣的實現方式。在ES5中用Object.getPrototypeOf函數得到一個對象的[[prototype]]。ES6中,使用Object.setPrototypeOf能夠直接修改一個對象的[[prototype]]。爲了方便,我下面的文章用__proto__來表明對象的[[prototype]]。spa

而prototype屬性是隻有函數才特有的屬性,當你建立一個函數時,js會自動爲這個函數加上prototype屬性,值是一個空對象。因此,函數在js中是很是特殊的,是所謂的一等公民。
那麼__proto__和prototype是怎麼聯繫起來的呢?讓咱們來看下下面的代碼:prototype

function Person(name, age) {
    this.name = name;
    this.age = age;
}
var person1 = new Person('jessica', 27);

當咱們new Person()的時候到底發生了什麼?
new一個構造函數,至關於實例化一個對象,這期間其實進行了這三個步驟:指針

建立對象,設爲o,即: var o = {};
上文提到了,每一個對象都有__proto__屬性,該屬性指向一個對象,這裏,將o對象的__Proto__指向構造函數Person的原型對象(Person.prototype);
將o做爲this去調用構造函數Person,從而設置o的屬性和方法並初始化。
當這3步完成,這個o對象就與構造函數Person再無聯繫,這個時候即便構造函數Person再加任何成員,都再也不影響已經實例化的o對象了。
此時,o對象具備了name和age屬性,同時具備了構造函數Person的原型對象的全部成員,固然,此時該原型對象是沒有成員的。code

如今你們都明白了吧,簡單的總結下就是:
js在建立對象的時候,都有一個叫作__proto__的內置屬性,用於指向建立它的函數對象的原型對象prototype
那麼一個對象的__proto__屬性究竟怎麼決定呢?答案顯而易見了:是由構造該對象的方法決定的。對象

建立對象的不一樣方法解析

下面講解三種常見的建立對象方法。繼承

對象字面量

好比:圖片

var Person = {
    name: 'jessica',
    age: 27
}

這種形式就是對象字面量,經過對象字面量構造出的對象,其__proto__指向Object.prototype。
因此,其實Object是一個函數也不難理解了。Object、Function都是是js自帶的函數對象。
能夠跑下面的代碼看看:

console.log(typeof Object); 
console.log(typeof Function);

構造函數

就如我前面講的,形如:

function Person(){}
var person1 = new Person();

這種形式建立對象的方式就是經過構造函數建立對象,這裏的構造函數是Person函數。上面也講過了,經過構造函數建立的對象,其__proto指向的是構造函數的prototype屬性指向的對象。

Object.create

var person1 = {
    name: 'jessica',
    age: 27
}
var person2 = Object.create(person1);

這種狀況下,person2的__proto__指向person1。在沒有Object.create函數的時候,人們大可能是這樣作的:

Object.create = function(p) {
    function f(){};
    f.prototype = p;
    return new f();
}

一看你們就會明白了。

總結

其實仔細思考下上面提到的三種建立對象的方法,追究其本質,不難發現,最根本的仍是利用構造函數再經過new來建立對象。所謂的對象字面量也只不過是語法糖而已,本質上是var o = new Object(); o.xx = xx;o.yy=yy;。 因此,函數真不愧是js中的一等公民呀~

原型鏈

既然已經提到了原型,就不得不提一下原型鏈了,畢竟這是實現繼承最關鍵所在,也是js對象精妙所在。
還記得上文提到的一個總結嗎?不記得?不要緊,我貼出來讓你們溫故而知新,哈哈~
js在建立對象的時候,都有一個叫作__proto__的內置屬性,用於指向建立它的函數對象的原型對象prototype
而原型鏈的基本思想就是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。
讓咱們再簡單回顧下構造函數、原型和實例的關係:
每一個構造函數都有一個原型對象,原型對象包含一個指向構造函數的指針(constructor),而實例則包含一個指向原型對象的內部指針(__proto__)。
咱們拿一個例子來說解:

function Person(name, age) {
    this.name = name;
    this.age = age;
}
var person1 = new Person('jessica', 27);

一圖勝前言,咱們用畫圖的形式來說解下上面的例子:
圖片描述

從上圖能夠看到,其實原型鏈的頂端是Object.prototype.__proto__,也即爲null。

總結

函數是js中的一等公民,js在建立對象的時候,都有一個叫作__proto__的內置屬性,用於指向建立它的函數對象的原型對象prototype。只有函數有prototype, 當你建立一個函數時,js會自動爲這個函數加上prototype屬性,值是一個空對象。

祝你們早日富可敵國,bye~

相關文章
相關標籤/搜索