輕松理解JavaScript原型到原型鏈

說到原型和原型鏈可能對于每個js初學(xué)者都是一件非常頭疼的事岸浑,因為它們之間的關(guān)系太繞了,網(wǎng)上的相關(guān)文章也是不計其數(shù)型将。本文將從另一個角度去理解什么是原型和原型鏈够挂。

我們先認(rèn)識一下什么是實例和構(gòu)造函數(shù)吧。

構(gòu)造函數(shù)創(chuàng)建對象


我們先使用構(gòu)造函數(shù)創(chuàng)建一個對象:


// 構(gòu)造函數(shù)使用的是大駝峰式寫法(首字母大寫)

    function Person() {

    };

    var person = new Person();

    person.name = 'charming';

    console.log(person.name);

在這個例子中Person就是構(gòu)造函數(shù)贷掖,而聲明的這個person就是對象實例嫡秕。

接下來就進(jìn)入主題:

prototype


每個函數(shù)都有prototype屬性,我們會在各種例子上見到它的身影苹威,比如:


function Person() {

};

//需要注意的是只有函數(shù)才有prototype屬性
Person.prototype.name = 'charming';

var person1 = new Person();
var person2 = new Person();

console.log(person1.name); // 'charming'

console.log(person2.name); // 'charming'

那函數(shù)的prototype屬性到底指向誰呢昆咽?是這個函數(shù)的原型嗎?

其實牙甫,這個函數(shù)的prototype指向的是一個對象掷酗,這個對象正是調(diào)用該構(gòu)造函數(shù)創(chuàng)建的實例的原型,也就是這個例子中的person1和person2的原型窟哺。

那原型是什么呢汇在?我們這樣理解:
每個JavaScript對象(null除外)創(chuàng)建的時候會與之關(guān)聯(lián)另一個對象,這個對象就是我們所說的******脏答,每個對象都會從原型上”繼承“屬性糕殉。
讓我們用一張圖來表示構(gòu)造函數(shù)和實例原型之間的關(guān)系:

prototype.png

在這張圖中我們用Object.prototype來表示實例原型。
那我們該怎么表示實例和實例原型殖告,也就是person和Person.prototype之間的關(guān)系呢阿蝶,這就用到我們要講的的第二個屬性了:

proto


每個JavaScript對象(null除外)都具有的一個屬性,叫_proto_這個屬性會指向該對象的原型黄绩。
為了證明這一點我們可以在火狐或者谷歌中測試一下

function Person() {

};
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true

現(xiàn)在我們再來更新一下關(guān)系:


__proto__.png

既然實例和構(gòu)造函數(shù)都可以指向原型羡洁,那原型是否有屬性指向構(gòu)造函數(shù)或者實例呢?

constructor


指向?qū)嵗故菦]有爽丹,因為一個構(gòu)造函數(shù)可以生成多個實例筑煮,但是原型指向構(gòu)造函數(shù)倒是可以,這就是我們要說的第三個屬性:constructor粤蝎,每個對象都有一個 constructor 屬性指向關(guān)聯(lián)的構(gòu)造函數(shù)真仲。
為了驗證這一點我們可以嘗試一下:

function Person() {
 };
console.log(Person === Person.prototype.constructor) // true

所以我們就再更新一下關(guān)系圖:


constructor.png

綜上我們得出:

function Person() {
};
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person) // true
// 順便用一個es5的方法,用于獲取對象原型
console.log(Object.getPrototypeOf(person) === Person.prototype); // true

了解了構(gòu)造函數(shù)初澎,實例原型秸应,和原型之間的關(guān)系,接下來我們就說一下實例和原型之間的關(guān)系:

實例與原型


當(dāng)讀取實例的屬性時,如果找不到 就會向與實例關(guān)聯(lián)的原型的屬性上查找软啼,一直找到最頂層為止桑谍。
舉個例子:

function Person() {
};
Person.prototype.name = 'charming'
var person = new Person();
person.name = 'Lyh';
console.log(person.name) // 'Lyh'
delete person.name
console.log(person.naem) // 'charming'

這個例子中我們給person添加了一個name屬性,當(dāng)我們打印preson.name這個屬性是當(dāng)然是 'Lyh' 祸挪,但是當(dāng)我刪除了person自身的nama屬性是锣披,再去訪問這個name屬性時,
但是當(dāng)我們刪除了 person 的 name 屬性時贿条,讀取 person.name雹仿,從 person 對象中找不到 name 屬性就會從 person 的原型也就是 person.__proto__ ,也就是 Person.prototype中查找闪唆,幸運的是我們找到了 name 屬性盅粪,結(jié)果為 'charming'钓葫。
如果還是沒找到呢悄蕾?原型的原型又是什么呢?

原型的原型


在前面我們也說了原型也是一個對象础浮,既然是對象帆调,我們就用最原始的方法創(chuàng)建它。例如:

var obj = new Object();
obj.name = 'charming';
console.log(obj.name) // charming

其實原型對象就是通過Object構(gòu)造函數(shù)生成的豆同,結(jié)合之前所說的番刊,實例的__proto__指向構(gòu)造函數(shù)的prototype,所以我們再更新一下關(guān)系圖:

原型的原型.png

原型鏈


那Object.prototype的原型呢影锈?
其實就是null了芹务,我們可以打印一下:

console.log(Object.prototype.__proto__ === null); // true

那 null 究竟代表了什么呢?
引用阮一峰老師的《undefined與null的區(qū)別》就是:
null表示“沒有對象”鸭廷,即該處不應(yīng)該有值枣抱。
所以O(shè)bject.prototype.__proto__的值為null跟Object.prototype沒有原型,其實表達(dá)了一個意思辆床。
所以查找屬性的時候查到Object.prototype就可以停止了佳晶。
最后我們再更新一下關(guān)系圖:

原型鏈.png

圖中由相互關(guān)聯(lián)的原型組成的鏈狀態(tài)結(jié)構(gòu)就是原型鏈,也就是藍(lán)色的這條線

補充一下

constructor

首先是constructor屬性讼载,我們看個例子:

function Person() {

};
var person = new Person();
console.log(person.constructor === Person); // true

當(dāng)獲取 person.constructor 時轿秧,其實person中并沒有 constructor 屬性,當(dāng)不能讀取到constructor屬性時咨堤,會從person的原型也就是Person.prototype中讀取菇篡,正好原型中有該屬性,所以:

person.constructor === Person.prototype.constructor

__proto__
其次是 __proto__ 一喘,絕大部分瀏覽器都支持這個非標(biāo)準(zhǔn)的方法訪問原型逸贾,然而它并不存在于 Person.prototype 中,實際上,它是來自于 Object.prototype 铝侵,與其說是一個屬性灼伤,不如說是一個 getter/setter,當(dāng)使用 obj.__proto__時咪鲜,可以理解成返回了 Object.getPrototypeOf(obj)狐赡。

這篇文章呢是我在另一片文章上收獲的覺得很好理解。就分享給你們了疟丙。
不喜勿噴颖侄!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市享郊,隨后出現(xiàn)的幾起案子览祖,更是在濱河造成了極大的恐慌,老刑警劉巖炊琉,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件展蒂,死亡現(xiàn)場離奇詭異,居然都是意外死亡苔咪,警方通過查閱死者的電腦和手機锰悼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來团赏,“玉大人箕般,你說我怎么就攤上這事√蚯澹” “怎么了丝里?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長体谒。 經(jīng)常有香客問我杯聚,道長,這世上最難降的妖魔是什么营密? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任械媒,我火速辦了婚禮,結(jié)果婚禮上评汰,老公的妹妹穿的比我還像新娘纷捞。我一直安慰自己,他們只是感情好被去,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布主儡。 她就那樣靜靜地躺著,像睡著了一般惨缆。 火紅的嫁衣襯著肌膚如雪糜值。 梳的紋絲不亂的頭發(fā)上丰捷,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音寂汇,去河邊找鬼病往。 笑死,一個胖子當(dāng)著我的面吹牛骄瓣,可吹牛的內(nèi)容都是我干的停巷。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼榕栏,長吁一口氣:“原來是場噩夢啊……” “哼畔勤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扒磁,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤庆揪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后妨托,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缸榛,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年始鱼,在試婚紗的時候發(fā)現(xiàn)自己被綠了仔掸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脆贵。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡医清,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卖氨,到底是詐尸還是另有隱情会烙,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布筒捺,位于F島的核電站柏腻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏系吭。R本人自食惡果不足惜五嫂,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肯尺。 院中可真熱鬧沃缘,春花似錦、人聲如沸则吟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氓仲。三九已至水慨,卻和暖如春得糜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晰洒。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工朝抖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谍珊。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓槽棍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抬驴。 傳聞我的和親對象是個殘疾皇子炼七,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容