JavaScript原型與繼承(一)

我曾嘗試理解關(guān)于prototype的相關(guān)概念乡革,最初理解起來晦澀難懂牵啦,加上當時用的地方又少亚情。后面漸漸明白,當你需要了解一個東西的時候哈雏,刻意的去理解是沒有本質(zhì)的作用的楞件,但是能在你的腦海里留下一絲印象,當你真正遇到的時候裳瘪,會想起曾經(jīng)看到過土浸,時機成熟的時候再去理解,會有不少收獲盹愚,輪番看個幾遍栅迄,拿上實例解析,會發(fā)現(xiàn)豁然開朗皆怕。

本文闡述的相關(guān)內(nèi)容:

  • 創(chuàng)建對象的幾種模式以及創(chuàng)建的過程
  • 原型鏈prototype的理解毅舆,以及prototype__proto__[[Prototype]])的關(guān)系
  • 繼承的幾種實現(xiàn)

1.常見模式與原型鏈的理解

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

function Test() {
    // 
}

流程

  • 創(chuàng)建函數(shù)的時候會默認為Test創(chuàng)建一個prototype屬性西篓,Test.prototype包含一個指針指向的是Object.prototype
  • prototype默認會有一個constructor,且Test.prototype.constructor = Test
  • prototype里的其它方法都是從Object繼承而來
示例
// 調(diào)用構(gòu)造函數(shù)創(chuàng)建實例
var instance = new Test()

此處的instance包含了一個指針指向構(gòu)造函數(shù)的原型憋活,(此處的指針在chrome里叫__proto__岂津,也等于[[Prototype]]

示例

b.原型模式
由上我們可以知道,默認創(chuàng)建的prototype屬性只擁有constructor和繼承至Object的屬性悦即,原型模式就是為prototype添加屬性和方法

Test.prototype.getName = ()=> {
    alert('name')
}

此時的instance實例就擁有了getName方法吮成,因為實例的指針是指向Test.prototype的

instance.__proto__ === Test.prototype

如下圖所示
![897RVF]E5@IX$)`IVJ3BOSY.png](http://upload-images.jianshu.io/upload_images/3637499-2c25e10269d8bbbd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

這里我們可得知:實例instance與構(gòu)造函數(shù)之間是通過原型prototype來相關(guān)聯(lián)的。

c.組合模式
這種模式我們用的最多辜梳,其實也是原型模式的另一種寫法粱甫,只不過有一點小區(qū)別而已

function Test() {}

Test.prototype = {
    getName() {
        alert('name')
    }
}

我們經(jīng)常會這么直接重寫prototype方法,由上我們可知作瞄,prototype會默認自帶constructor屬性指向構(gòu)造函數(shù)本身茶宵,那么重寫以后呢?

Test.prototype.constructor === Object 
// 而并不等于Test了
// 因為重寫以后相當于利用字面量方式創(chuàng)建一個實例對象宗挥,這個實例的構(gòu)造函數(shù)是指向Object本身的

當然我們也可以手動賦值constructor

Test.prototype = {
    constructor: Test,
    getName() {
        alert('name')
    }
}

那么又會有疑問了constructor要不要有何意義乌庶?我覺得constructor意義僅僅是為了來鑒別原型所屬的構(gòu)造函數(shù)吧。

當需要獲取某個屬性的時候契耿,會先從實例中查找瞒大,沒有就根據(jù)指針所指向的原型去查找,依次向上搪桂,直到實例的指針__proto__指向為null時停止查找透敌,例如:

// 1 讀取name
instance.name 

// 2 instance.__proto__ === Test.prototype
Test.prototype.name

// 3 Test.prototype.__proto__ === Object.prototype
Object.prototype.name

// 4
Object.prototype.__proto__ === null

當找到了這個屬性就會直接返回,而不會繼續(xù)查找锅棕,即使這個屬性值為null拙泽,想要繼續(xù)查找,我們可以通過delete操作符來實現(xiàn)裸燎。

由這里我們自然可以想到Array, Date, Function, String顾瞻,都是一個構(gòu)造函數(shù),他們的原型的指針都是指向Object.prototype德绿,它們就像我這里定義的Test一樣荷荤,只不過是原生自帶而已

d.幾個有用的方法

  • Object.getPrototypeOf() 獲取某個實例的指針所指向的原型
Object.getPrototypeOf(instance) === Test.prototype
  • hasOwnProperty 判斷一個屬性是存在于實例中還是存在于原型中,如圖所示:
    NY~N}CNR`}8W%4QA$M8LFE4.png
  • in操作符移稳,無論該屬性是否可枚舉
'name' in instance  // true
'getName' in instance // true

無論屬性是在實例中蕴纳,還是在原型中都返回true,所以當我們需要判斷一個屬性存在與實例中个粱,還是原型中有2種辦法

// 一種就是使用hasOwnProperty判斷在實例中
// 另一種判斷在原型中
instance.hasOwnProperty('getName') === false && 'getName' in instance === true
  • for ... in操作符也是一樣的古毛,但只會列出可枚舉的屬性,ie8版本的bug是無論該屬性是否可枚舉,都會列出

    D(%S__GN8404{H9X6PW$DVK.png

    name是在實例中定義的稻薇,getName是在原型中定義的

  • Object.keys()則不一樣嫂冻,它返回一個對象上所有可枚舉的屬性,僅僅是該實例中的

Object.keys(instance)
// ["name"]

e.總結(jié)
以上討論了構(gòu)造函數(shù)塞椎,原型和實例的關(guān)系:

  • 每個構(gòu)造函數(shù)都有原型對象
  • 每個原型對象都有一個constructor指針指向構(gòu)造函數(shù)
  • 每個實例都有一個__proto__指針指向原型

2.繼承

繼承的實質(zhì)是利用構(gòu)造函數(shù)的原型 = 某個構(gòu)造函數(shù)的實例桨仿,以此來形成原型鏈。例如

// 定義父類
function Parent() {}
Parent.prototype.getName = ()=> {
    console.log('parent')
}
// 實例化父類
let parent = new Parent()

// 定義子類
function Child() {}
Child.prototype = parent 
// 實例化子類
let child = new Child()

child.getName() // parent
// 此時
child.constructor === parent.constructor === Parent

a.最經(jīng)典的繼承模式

function Parent(name) {
    this.name = name
    this.colors = ['red']
}
Parent.prototype.getName = function() {
    console.log(this.name)
}
// 實例化父類
let parent = new Parent()

function Child(age, name) {
    Parent.call(this, name)
    this.age = age
}
Child.prototype = parent 
// 實例化子類
let child = new Child(1, 'aaa')
child.getName() // parent

這里會讓我想到ES6中的class繼承

class Parent {
    constructor(name) {
        this.name = name
        this.colors = ['red']
    }
    getName() {
        console.log(this.name)
    }
}

class Child extends Parent {
    constructor(age, name) {
        super(name)
    }
}

let child = new Child(1, 'aaa')
child.getName() // parent

其實是一個道理案狠,這里我們不難想到服傍,將Child.prototype指向parent實例,就是利用原型實現(xiàn)的繼承骂铁,而為了每個實例都擁有各自的colors和name吹零,也就是基礎(chǔ)屬性,在Child的構(gòu)造函數(shù)中call調(diào)用了Parent的構(gòu)造函數(shù)从铲,相當于每次實例化的時候都初始化一遍colors和name瘪校,而不是所有實例共享原型鏈中的colors和name


以上也是自己一邊學(xué)習一邊整理的,邏輯有點混亂名段,見諒,還望有誤之處指出泣懊,不勝感激伸辟!

參考:
紅寶書第六章
MDN 繼承與原型鏈
理解JavaScript的原型鏈和繼承

相關(guān)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市馍刮,隨后出現(xiàn)的幾起案子信夫,更是在濱河造成了極大的恐慌,老刑警劉巖卡啰,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件静稻,死亡現(xiàn)場離奇詭異,居然都是意外死亡匈辱,警方通過查閱死者的電腦和手機振湾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亡脸,“玉大人押搪,你說我怎么就攤上這事∏衬耄” “怎么了大州?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長垂谢。 經(jīng)常有香客問我厦画,道長,這世上最難降的妖魔是什么滥朱? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任根暑,我火速辦了婚禮力试,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘购裙。我一直安慰自己懂版,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布躏率。 她就那樣靜靜地躺著躯畴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪薇芝。 梳的紋絲不亂的頭發(fā)上蓬抄,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音夯到,去河邊找鬼嚷缭。 笑死,一個胖子當著我的面吹牛耍贾,可吹牛的內(nèi)容都是我干的阅爽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼荐开,長吁一口氣:“原來是場噩夢啊……” “哼付翁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晃听,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤百侧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后能扒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佣渴,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年初斑,在試婚紗的時候發(fā)現(xiàn)自己被綠了辛润。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡越平,死狀恐怖频蛔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秦叛,我是刑警寧澤晦溪,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站挣跋,受9級特大地震影響三圆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一舟肉、第九天 我趴在偏房一處隱蔽的房頂上張望修噪。 院中可真熱鬧,春花似錦路媚、人聲如沸黄琼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脏款。三九已至,卻和暖如春裤园,著一層夾襖步出監(jiān)牢的瞬間撤师,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工拧揽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剃盾,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓淤袜,卻偏偏與公主長得像痒谴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子铡羡,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

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