JavaScript的原型和原型鏈

原型和原型鏈也是一個老生常談的問題,很多初學者對于__proto__和prototype搞不清楚。其實我剛開始學習的時候也搞不清楚,因此寫這篇文章對于這方面的知識進行一個鞏固。

作者:一木

鏈接:http://www.lofter.com/lpost/1f0227be_1106b61c

來源:LOFTER

在說原型和原型鏈之前,我們先來看看如下知識點:

一、私有變量和私有函數(shù)

在函數(shù)內(nèi)部定義的變量和函數(shù),如果不對外提供接口,那么外部是無法訪問到的。這個被定義的變量和函數(shù)就叫做該函數(shù)的私有變量和私有函數(shù)。

function Foo()?{

var name =?"yiMu";??//私有變量

var fn = function()?{???//私有函數(shù)

console.log("hello word");

};

}

var bar = new Foo();

console.log(bar.name);??//undefined

console.log(bar.fn);????//undefined

二、靜態(tài)變量和靜態(tài)函數(shù)

當定義一個函數(shù)后通過"."的方式為其添加屬性和函數(shù),通過對象本身可以訪問到,但是其實例卻無法訪問到,這樣的變量和函數(shù)叫做靜態(tài)變量和靜態(tài)函數(shù)。

function Foo(){}

Foo.num = 10;??//靜態(tài)變量

Foo.fn = function()?{???//靜態(tài)函數(shù)

console.log("hello word");

};

console.log(Foo.num);??//10

console.log(typeof Foo.fn);?//function

var bar = new Foo();

console.log(bar.num);??//undefined

console.log(typeof bar.fn);?//undefined

三、實例屬性和實例方法

在面向?qū)ο缶幊讨谐艘恍旌瘮?shù),我們還是希望在定義一個對象的時候同時定義一些屬性和方法并在實例化后能夠訪問,這些添加的屬性和方法就叫做實例屬性和實例方法。

function Foo()?{

this.num =?[];??//實例屬性

this.fn = function()?{??//實例方法

console.log("hello word");

};

}

console.log(Foo.num);???//undefined

console.log(typeof Foo.fn);?//undefined

var bar = new Foo();

console.log(bar.num);???//[]

console.log(typeof bar.fn);?//function

我們也可以為實例屬性和實例方法添加屬性和方法:

function Foo()?{

this.num =?[];??//實例屬性

this.fn = function()?{??//實例方法

console.log("hello word");

}

}

var oneBar = new Foo();

oneBar.num.push(1);

oneBar.fn =?{};

console.log(oneBar.num);????//[1]

console.log(typeof oneBar.fn);??//Object

var twoBar = new Foo();

console.log(twoBar.num);???//[]

console.log(typeof twoBar.fn);??//function

從上面的代碼可以看到,當我們在oneBar中修改了num的值和fn的類型,但是在twoBar中卻沒有發(fā)生改變,這是由于數(shù)組和函數(shù)都是對象,屬于引用類型。oneBar和twoBar中的屬性和方法名稱雖然相同但是卻不是同一個引用,它們只是對Foo對象定義的屬性和方法的一個復制。

如果一個構(gòu)造函數(shù)對象有上千的實例方法,那么它的每個實例都要對這個構(gòu)造函數(shù)的上千個實例方法進行復制,這顯然是不科學的,那么這種情況下我們就必須使用prototype了。

基本概念

我們創(chuàng)建的每一個函數(shù)都有一個prototype屬性,這個屬性是一個指針,指向一個對象,這個對象的用途是可以由特定類型的所有實例共享的屬性和方法,那么prototype就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個對象實例的原型對象。

使用原型的好處就是可以讓對象實例共享它所包含的屬性和方法。

在JS中,一共有兩種類型的值——原始值和對象值。每個對象都有一個內(nèi)部屬性prototype,我們稱之為原型。原型的值可以是一個對象,也可以是null。如果它的值是一個對象,那么這個對象一定也有自己的原型,這樣就形成了一條線性的鏈,我們稱之為原型鏈。

prototype和__proto__的區(qū)別

函數(shù)可以作為構(gòu)造函數(shù)來使用,只有函數(shù)的prototype屬性才可以訪問到,但是對象實例不具有該屬性,只有一個內(nèi)部的不可訪問的__proto__屬性,如下圖:

圖片來自于水乙博客

var a =?{};

console.log(a.prototype);???//undefined

console.log(a.__proto__);???//Object{}

var b = function(){};

console.log(b.prototype);???//Object{constructor: function, __proto__: Object}

console.log(b.__proto__);???//function(){}

圖片來自于水乙博客

//字面量方式

var a =?{};

console.log(a.__proto__);???//Object{}

console.log(a.__proto__?=== a.constructor.prototype);???//true

//構(gòu)造器方式

var C = function(){};

var d = new C();

console.log(d.__proto__);???//C{}

console.log(d.__proto__ === C.prototype);???//true

console.log(d.__proto__ === d.constructor.prototype);???//true

//Object.create方式

var e =?{name:?"qtb"};

var f = Object.create(e);

console.log(f.__proto__);???//Object{name:?"qtb"}

console.log(f.__proto__ === f.constructor.prototype);???//false(此處即為圖1中例外情況)

圖片來自于水乙博客

var A = function(){};

var a = new A();

console.log(a.__proto__);???//A{}(即構(gòu)造器function A 的原型對象)

console.log(a.__proto__.__proto__);?//Object{}(即構(gòu)造器function Object 的原型對象)

console.log(a.__proto__.__proto__.__proto__);???//null

構(gòu)造函數(shù)、實例和原型對象的區(qū)別

實例就是通過構(gòu)造函數(shù)創(chuàng)建的。實例一創(chuàng)建出來就具有constructor屬性(指向構(gòu)造函數(shù))和__proto__屬性(指向原型對象)。構(gòu)造函數(shù)中有一個prototype屬性,這個屬性是一個指針,指向它的原型對象。

原型對象內(nèi)部也有一個指針(constructor屬性),指向構(gòu)造函數(shù)Person.prototype.constructor = Person。

實例可以訪問原型對象上定義的屬性和方法。

如果你在本文中發(fā)現(xiàn)錯誤或者有異議的地方,可以在評論區(qū)留言,謝謝!

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容