理解 javascript 的原型鏈及繼承

理解 javascript 的原型鏈及繼承

class Shape{}
class Circle extends Shape{}
const circle = new Circle();

console.log(circle.__proto__ === Circle.prototype);
console.log(Circle.prototype.__proto__ === Shape.prototype);
console.log(Shape.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null);

console.log(Circle.__proto__ === Shape);
console.log(Shape.__proto__ === Function.prototype);
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null);

console.log(circle.constructor === Circle)

以上所有的運(yùn)行結(jié)果都是 true;

三種構(gòu)造對(duì)象的方法:

  1. 通過對(duì)象字面量構(gòu)造诫欠。
var person1 = {
    name: 'cyl',
    sex: 'male'
};

形如這個(gè)形式的叫做對(duì)象字面量翁垂。這樣子構(gòu)造出的對(duì)象匀油,其[[prototype]]指向Object.prototype

  1. 構(gòu)造函數(shù)構(gòu)造凑懂。
function Person(){}
var person1 = new Person();

通過new操作符調(diào)用的函數(shù)就是構(gòu)造函數(shù)。由構(gòu)造函數(shù)構(gòu)造的對(duì)象虽惭,其[[prototype]]指向其構(gòu)造函數(shù)的prototype屬性指向的對(duì)象橡类。每個(gè)函數(shù)都有一個(gè)prototype屬性,其所指向的對(duì)象帶有constructor屬性芽唇,這一屬性指向函數(shù)自身顾画。(在本例中,person1的[[prototype]]指向Person.prototype)

  1. 函數(shù)Object.create構(gòu)造的匆笤。
var person1 = {
    name: 'cyl',
    sex: 'male'
};

var person2 = Object.create(person1);

本例中研侣,對(duì)象person2的[[prototype]]指向?qū)ο髉erson1。在沒有Object.create函數(shù)的日子里疚膊,人們是這樣做的:

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

Object 對(duì)象的屬性

屬性 描述
constructor 返回創(chuàng)建該對(duì)象的構(gòu)造函數(shù)义辕。
prototype 返回創(chuàng)建該對(duì)象的函數(shù)的原型對(duì)象。

一寓盗、原型

__proto__(隱式原型)

每個(gè)JS對(duì)象一定對(duì)應(yīng)一個(gè)原型對(duì)象灌砖,并從原型對(duì)象繼承屬性和方法。

對(duì)象proto屬性的值就是它所對(duì)應(yīng)的原型對(duì)象傀蚌,隱式原型指向創(chuàng)建這個(gè)對(duì)象的函數(shù)(constructor)的prototype基显。

JavaScript中任意對(duì)象都有一個(gè)內(nèi)置屬性[[prototype]],在ES5之前沒有標(biāo)準(zhǔn)的方法訪問這個(gè)內(nèi)置屬性善炫,但是大多數(shù)瀏覽器都支持通過proto來訪問撩幽。ES5中有了對(duì)于這個(gè)內(nèi)置屬性標(biāo)準(zhǔn)的Get方法Object.getPrototypeOf().
Object.prototype 這個(gè)對(duì)象是個(gè)例外,它的proto值為null

var one = {x: 1};
var two = new Object();
console.log(one.__proto__ === Object.prototype)//true
console.log(two.__proto__ === Object.prototype)//true

作用:

隱式原型的作用:構(gòu)成原型鏈箩艺,同樣用于實(shí)現(xiàn)基于原型的繼承窜醉。舉個(gè)例子,當(dāng)我們?cè)L問obj這個(gè)對(duì)象中的x屬性時(shí)艺谆,如果在obj中找不到榨惰,那么就會(huì)沿著proto依次查找。

prototype(顯式原型)

不像每個(gè)對(duì)象都有proto屬性來標(biāo)識(shí)自己所繼承的原型静汤,只有函數(shù)才有prototype屬性琅催。

JS不像其它面向?qū)ο蟮恼Z言,它沒有類(class虫给,ES6引進(jìn)了這個(gè)關(guān)鍵字藤抡,但更多是語法糖)的概念。JS通過函數(shù)來模擬類抹估。

當(dāng)你創(chuàng)建函數(shù)時(shí)缠黍,JS會(huì)為這個(gè)函數(shù)自動(dòng)添加prototype屬性,值是空對(duì)象药蜻。而一旦你把這個(gè)函數(shù)當(dāng)作構(gòu)造函數(shù)(constructor)調(diào)用(即通過new關(guān)鍵字調(diào)用)瓷式,那么JS就會(huì)幫你創(chuàng)建該構(gòu)造函數(shù)的實(shí)例,實(shí)例繼承構(gòu)造函數(shù)prototype的所有屬性和方法(實(shí)例通過設(shè)置自己的proto指向承構(gòu)造函數(shù)的prototype來實(shí)現(xiàn)這種繼承)谷暮。

JS是單繼承的蒿往,Object.prototype是原型鏈的頂端,所有對(duì)象從它繼承了包括toString等等方法和屬性湿弦。Object.prototype.proto === null瓤漏,說明原型鏈到Object.prototype終止。

作用:

顯式原型的作用:用來實(shí)現(xiàn)基于原型的繼承與屬性的共享颊埃。


Object本身是構(gòu)造函數(shù)蔬充,繼承了Function.prototype;Function也是對(duì)象,繼承了Object.prototype班利。

Object instanceof Function // true
Function instanceof Object // true

instanceof 運(yùn)算符用來測(cè)試一個(gè)對(duì)象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的prototype 屬性饥漫。

ES規(guī)范是怎么說的?

Function本身就是函數(shù)罗标,F(xiàn)unction.proto是標(biāo)準(zhǔn)的內(nèi)置對(duì)象Function.prototype庸队。
Function.prototype.proto是標(biāo)準(zhǔn)的內(nèi)置對(duì)象Object.prototype积蜻。

例一

//函數(shù)聲明創(chuàng)一個(gè)空函數(shù)
function foo(){}

3229842-48de74a3cfec7e9d

foo這個(gè)函數(shù)就有個(gè)prototype屬性,并且它默認(rèn)有兩個(gè)屬性:constructor和proto彻消。constructor屬性會(huì)指向它本身foo竿拆,proto是在chrome中暴露的(不是一個(gè)標(biāo)準(zhǔn)屬性),那么foo.prototype的原型會(huì)指向Object.prototype宾尚。因此Object.prototype上的一些方法toString丙笋,valueOf才會(huì)被每個(gè)一般的對(duì)象所使用。

例二

function foo(){}
foo.prototype.x = 1;
var obj3 = new foo();//實(shí)例對(duì)象
console.log(obj3.x); //1

我們這里有個(gè)foo函數(shù)煌贴,這個(gè)函數(shù)有個(gè)prototype的對(duì)象屬性御板,它的作用就是當(dāng)使用new foo()去構(gòu)造實(shí)例的時(shí)候,這個(gè)構(gòu)造器的prototype屬性會(huì)用作new出來的這些對(duì)象的原型牛郑。

在這個(gè)例子中怠肋,obj3.proto===foo.prototype,即,每個(gè)對(duì)象都有一個(gè)proto屬性井濒,指向創(chuàng)建該對(duì)象的函數(shù)的prototype灶似。Object.prototype是一個(gè)特例,它的proto指向的是null瑞你。

例三

// 聲明構(gòu)造函數(shù)
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 通過prototye屬性酪惭,將方法放到原型對(duì)象上
Person.prototype.getName = function() {
    return this.name;
}

var p1 = new Person('tim', 10);
var p2 = new Person('jak', 22);
3229842-3e7187f42cdfcda9

構(gòu)造函數(shù)的prototype與所有實(shí)例對(duì)象的proto都指向原型對(duì)象。而原型對(duì)象的constructor指向構(gòu)造函數(shù)者甲。

二春感、原型鏈

每一個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,當(dāng)我們讓某一原型對(duì)象等于另一構(gòu)造函數(shù)的實(shí)例虏缸,此時(shí)該原型對(duì)象就包含一個(gè)指針鲫懒,該指針指向這一構(gòu)造函數(shù)的原型對(duì)象,該指針指向的原型對(duì)象中包含一個(gè)指向這一構(gòu)造函數(shù)的指針刽辙,同樣我們可以令該指針指向的原型對(duì)象等于另一構(gòu)造函數(shù)的實(shí)例窥岩,如此遞進(jìn),則形成一條實(shí)例與原型的鏈條宰缤,即原型鏈颂翼。

 function Parent() {
        this.name = 'parent';
    };
    Parent.prototype.getName = function() {
        return this.name;
    };
    function Child() {
        this.childname = 'child';
    }
    Child.prototype = new Parent();
    Child.prototype.getChildName = function() {
        return this.childname;
    };
    var child = new Child();
    console.log(child.getName()); //輸出parent
    console.log(child.getChildName());  //輸出child

在上面的例子中,child實(shí)例指向Child原型慨灭,Child原型等于Parent實(shí)例朦乏,即指向Parent原型⊙踔瑁可見本質(zhì)即是以一個(gè)構(gòu)造函數(shù)的實(shí)例重寫原型對(duì)象呻疹,形成原型鏈。

總結(jié)

1筹陵、所有的對(duì)象都有 proto 屬性刽锤,該屬性對(duì)應(yīng)該對(duì)象的原型镊尺;
2、所有的函數(shù)對(duì)象都有 prototype 屬性姑蓝,該屬性的值會(huì)被賦值給該函數(shù)創(chuàng)建的對(duì)象的 proto屬性鹅心;
3吕粗、所有的原型對(duì)象都有constructor屬性纺荧,該屬性對(duì)應(yīng)創(chuàng)建所有指向該原型的實(shí)例的構(gòu)造函數(shù);
4颅筋、函數(shù)對(duì)象和原型對(duì)象通過prototype和constructor屬性進(jìn)行相互關(guān)聯(lián)宙暇。

extends

用關(guān)鍵詞extends聲明子類關(guān)系:

    class Circle extends Shape {}

extends后面可以接駁任意合法且擁有prototype屬性的構(gòu)造函數(shù)。它可以是:

另一個(gè)類
源自現(xiàn)有繼承框架(譯者注:作者指的是原型繼承议泵,即使在JavaScript中類繼承的本質(zhì)也是原型繼承)的近類函數(shù)
一個(gè)普通的函數(shù)
一個(gè)包含一個(gè)函數(shù)或類的變量
一個(gè)對(duì)象上的屬性訪問
一個(gè)函數(shù)調(diào)用

圖示

  1. 在JS里占贫,萬物皆對(duì)象。方法(Function)是對(duì)象先口,方法的原型(Function.prototype)是對(duì)象型奥。因此,它們都會(huì)具有對(duì)象共有的特點(diǎn)碉京。即:對(duì)象具有屬性proto厢汹,可稱為隱式原型,一個(gè)對(duì)象的隱式原型指向構(gòu)造該對(duì)象的構(gòu)造函數(shù)的原型谐宙,這也保證了實(shí)例能夠訪問在構(gòu)造函數(shù)原型中定義的屬性和方法烫葬。
  2. 方法(Function)方法這個(gè)特殊的對(duì)象,除了和其他對(duì)象一樣有上述proto屬性之外凡蜻,還有自己特有的屬性——原型屬性(prototype)搭综,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象划栓,這個(gè)對(duì)象的用途就是包含所有實(shí)例共享的屬性和方法(我們把這個(gè)對(duì)象叫做原型對(duì)象)兑巾。原型對(duì)象也有一個(gè)屬性,叫做constructor忠荞,這個(gè)屬性包含了一個(gè)指針蒋歌,指回原構(gòu)造函數(shù)。

上圖解析

1.構(gòu)造函數(shù)Foo()構(gòu)造函數(shù)的原型屬性Foo.prototype指向了原型對(duì)象钻洒,在原型對(duì)象里有共有的方法奋姿,所有構(gòu)造函數(shù)聲明的實(shí)例(這里是f1,f2)都可以共享這個(gè)方法素标。
2.原型對(duì)象Foo.prototypeFoo.prototype保存著實(shí)例共享的方法称诗,有一個(gè)指針constructor指回構(gòu)造函數(shù)。
3.實(shí)例f1和f2是Foo這個(gè)對(duì)象的兩個(gè)實(shí)例头遭,這兩個(gè)對(duì)象也有屬性proto寓免,指向構(gòu)造函數(shù)的原型對(duì)象癣诱,這樣子就可以像上面1所說的訪問原型對(duì)象的所有方法啦。另外:構(gòu)造函數(shù)Foo()除了是方法袜香,也是對(duì)象啊撕予,它也有proto屬性,指向誰呢蜈首?指向它的構(gòu)造函數(shù)的原型對(duì)象唄实抡。函數(shù)的構(gòu)造函數(shù)不就是Function嘛,因此這里的proto指向了Function.prototype欢策。其實(shí)除了Foo()吆寨,F(xiàn)unction(), Object()也是一樣的道理。原型對(duì)象也是對(duì)象啊踩寇,它的proto屬性啄清,又指向誰呢?同理俺孙,指向它的構(gòu)造函數(shù)的原型對(duì)象唄辣卒。這里是Object.prototype.最后,Object.prototype的proto屬性指向null睛榄。

總結(jié):

  • 對(duì)象有屬性proto,指向該對(duì)象的構(gòu)造函數(shù)的原型對(duì)象荣茫。
  • 方法除了有屬性proto,還有屬性prototype,prototype指向該方法的原型對(duì)象懈费。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末计露,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子憎乙,更是在濱河造成了極大的恐慌票罐,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泞边,死亡現(xiàn)場(chǎng)離奇詭異该押,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)阵谚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門蚕礼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人梢什,你說我怎么就攤上這事奠蹬。” “怎么了嗡午?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵囤躁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)狸演,這世上最難降的妖魔是什么言蛇? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮宵距,結(jié)果婚禮上腊尚,老公的妹妹穿的比我還像新娘。我一直安慰自己满哪,他們只是感情好婿斥,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翩瓜,像睡著了一般受扳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兔跌,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音峡蟋,去河邊找鬼坟桅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蕊蝗,可吹牛的內(nèi)容都是我干的仅乓。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蓬戚,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼夸楣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起子漩,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤豫喧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后幢泼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體紧显,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年缕棵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了孵班。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡招驴,死狀恐怖篙程,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情别厘,我是刑警寧澤虱饿,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響郭厌,放射性物質(zhì)發(fā)生泄漏袋倔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一折柠、第九天 我趴在偏房一處隱蔽的房頂上張望宾娜。 院中可真熱鬧,春花似錦扇售、人聲如沸前塔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽华弓。三九已至,卻和暖如春困乒,著一層夾襖步出監(jiān)牢的瞬間寂屏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工娜搂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迁霎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓百宇,卻偏偏與公主長(zhǎng)得像考廉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子携御,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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