你不知道的JavaScript之原型

第五章几晤、原型

JavaScript中的對象有一個特殊的[[Prototype]]內(nèi)置屬性, 其實(shí)就是對于其他對象的引用律姨。幾乎所有的對象在創(chuàng)建時[[Prototype]]屬性都會被賦予一個非空的值。

Object.prototype

所有普通的[[Prototype]]鏈最終都會指向內(nèi)置的Object.prototype。由于所有的“ 普通”(內(nèi)置泼橘, 不是特定主機(jī)的擴(kuò)展)對象都“ 源于”(或者說把[[Prototype]]鏈的頂端設(shè)置為)這個Object.prototype對象,所以它包含JavaScript中許多通用的功能迈勋。

Object.create() 建立兩個對象之間的連接

const parentObj = {
  name: 'parent'
}

const obj = Object.create(parentObj)
obj.name = 'obj'

屬性的設(shè)置和屏蔽

上段代碼中炬灭,執(zhí)行 obj.name = 'obj' 時,會出現(xiàn)以下情況:

  • parentObj作為obj的隱式原型靡菇,存在 name 屬性重归,并且沒有標(biāo)記為只讀(writable: false),那么會直接在 obj 中添加一個名為 name 的新屬性厦凤,即屏蔽屬性鼻吮。
const parentObj = {
  name: 'parent'
}

const obj = Object.create(parentObj)
obj.name = 'obj'
  • parentObj作為obj的隱式原型,存在 name 屬性泳唠,并且標(biāo)記為只讀(writable: false)狈网,那么無法在obj上添加新屬性,賦值語句會被忽略笨腥,嚴(yán)格模式下會拋出錯誤拓哺。總之脖母,不會產(chǎn)生屏蔽屬性士鸥。
const parentObj = {}
Object.defineProperty(parentObj, 'name', {
  value: 'parent',
  writable: false,
})

const obj = Object.create(parentObj)
obj.name = 'obj'
console.log(obj)
  • parentObj作為obj的隱式原型,存在 name 屬性谆级,并且它是一個setter烤礁,,那就一定會調(diào)用這個setter肥照。不會在obj中添加或修改屬性脚仔。
const parentObj = {}
Object.defineProperty(parentObj, 'name', {
  set(val) {
    console.log(val)
  },
})
const obj = Object.create(parentObj)
obj.name = 'obj'

原型

“類”

這個對象是在調(diào)用new Foo()時創(chuàng)建的,最后會被(有點(diǎn)武斷地)關(guān)聯(lián)到這個“Foo點(diǎn)prototype”對象上舆绎。

一些觀點(diǎn):

在面向類的語言中鲤脏,類可以被復(fù)制(或者說實(shí)例化)多次,就像用模具制作東西一樣。之所以會這樣是因?yàn)閷?shí)例化(或者繼承)一個類就意味著“ 把類的行為復(fù)制到物理對象中”猎醇,對于每一個新實(shí)例來說都會重復(fù)這個過程窥突。
但是在JavaScript中,并沒有類似的復(fù)制機(jī)制硫嘶。你不能創(chuàng)建一個類的多個實(shí)例阻问,只能創(chuàng)建多個對象,它們[[Prototype]]關(guān)聯(lián)的是同一個對象沦疾。但是在默認(rèn)情況下并不會進(jìn)行復(fù)制称近,因此這些對象之間并不會完全失去聯(lián)系,它們是互相關(guān)聯(lián)的曹鸠。
new Foo()會生成一個新對象(我們稱之為a)煌茬,這個新對象的內(nèi)部鏈接[[Prototype]]關(guān)聯(lián)的是Foo.prototype對象。
最后我們得到了兩個對象彻桃, 它們之間互相關(guān)聯(lián)坛善, 就是這樣。 我們并沒有初始化一個類邻眷, 實(shí)際上我們并沒有從“類”中復(fù)制任何行為到一個對象中眠屎,只是讓兩個對象互相關(guān)聯(lián)。
實(shí)際上肆饶,絕大多數(shù)JavaScript開發(fā)者不知道的秘密是改衩,new Foo()這個函數(shù)調(diào)用實(shí)際上并沒有直接創(chuàng)建關(guān)聯(lián), 這個關(guān)聯(lián)只是一個意外的副作用驯镊。new Foo()只是間接完成了我們的目標(biāo):一個關(guān)聯(lián)到其他對象的新對象葫督。

JavaScript中并沒有類的概念,所有這些關(guān)于類的說法都是荒謬的板惑,不準(zhǔn)確的橄镜。原型歸根到底是建立兩個對象之間的連接,這才是js本質(zhì)上的東西冯乘。

作者的看法:

因此我認(rèn)為這個容易混淆的組合術(shù)語“ 原型繼承”(以及使用其他面向類的術(shù)語比如“類”洽胶、“構(gòu)造函數(shù)”、“實(shí)例”裆馒、“多態(tài)”姊氓,等等)嚴(yán)重影響了大家對于JavaScript機(jī)制真實(shí)原理的理解。
繼承意味著復(fù)制操作喷好,JavaScript(默認(rèn))并不會復(fù)制對象屬性翔横。 相反,JavaScript會在兩個對象之間創(chuàng)建一個關(guān)聯(lián)梗搅, 這樣一個對象就可以通過委托訪問另一個對象的屬性和函數(shù)棕孙。委托(參見第6章)這個術(shù)語可以更加準(zhǔn)確地描述JavaScript中對象的關(guān)聯(lián)機(jī)制。

構(gòu)造函數(shù)

function Foo() {}
const foo = new Foo()
foo.constructor   // Foo
/**
 * 實(shí)際上foo本身并沒有.constructor屬性些膨。
 * 而且蟀俊,雖然foo.constructor確實(shí)指向Foo函數(shù),但是這個屬性并不是表示a由Foo“構(gòu)造”订雾。
 *  實(shí)際上肢预,.constructor引用同樣被委托給了Foo.prototype,而Foo.prototype.constructor默認(rèn)指向Foo洼哎。
 */

構(gòu)造函數(shù)vs普通函數(shù)

實(shí)際上烫映,F(xiàn)oo和你程序中的其他函數(shù)沒有任何區(qū)別。 函數(shù)本身并不是構(gòu)造函數(shù)噩峦, 然而锭沟, 當(dāng)你在普通的函數(shù)調(diào)用前面加上new關(guān)鍵字之后, 就會把這個函數(shù)調(diào)用變成一個“ 構(gòu)造函數(shù)調(diào)用” 识补。實(shí)際上族淮,new會劫持所有普通函數(shù)并用構(gòu)造對象的形式來調(diào)用它。
換句話說凭涂,在JavaScript中對于“構(gòu)造函數(shù)”最準(zhǔn)確的解釋是祝辣,所有帶new的函數(shù)調(diào)用。
函數(shù)不是構(gòu)造函數(shù)切油,但是當(dāng)且僅當(dāng)使用new時蝙斜,函數(shù)調(diào)用會變成“構(gòu)造函數(shù)調(diào)用”。

如何建立兩個對象之間的連接

  • es5 Object.create(source)
const foo = {
  name: 'foo',
  getName: function () {
    console.log(this.name)
  }
}

const bar = Object.create(foo)
bar.name = 'bar'
bar.getName()
  • es6 Object.setPrototypeOf(target, source)
const foo = {
  name: 'foo',
  getName: function () {
    console.log(this.name)
  }
}
const bar = {
  name: 'bar'
}
Object.setPrototypeOf(bar, foo)
bar.getName()

檢查“類”之間的關(guān)系

instanceof

obj instanceof Foo
左邊是一個對象澎胡,右邊是一個函數(shù)
實(shí)現(xiàn)的意義:在a的整條[[Prototype]]鏈中是否有指向Foo.prototype的對象

  • 這個方法只能處理對象(a)和函數(shù)( 帶.prototype引用的Foo)之間的關(guān)系孕荠。 如果你想判斷兩個對象(比如a和b)之間是否通過[[Prototype]]鏈關(guān)聯(lián), 只用instanceof無法實(shí)現(xiàn)攻谁。*

對象關(guān)聯(lián)

原型鏈:如果在對象上沒有找到需要的屬性或者方法引用稚伍,引擎就會繼續(xù)在[[Prototype]]關(guān)聯(lián)的對象上進(jìn)行查找。同理巢株,如果在后者中也沒有找到需要的引用就會繼續(xù)查找它的[[Prototype]]槐瑞,以此類推。這一系列對象的鏈接被稱為“原型鏈”阁苞。

Object.create()

Object.create(..)會創(chuàng)建一個新對象(bar)并把它關(guān)聯(lián)到我們指定的對象(foo)困檩,這樣我們就可以充分發(fā)揮[[Prototype]]機(jī)制的威力( 委托)并且避免不必要的麻煩( 比如使用new的構(gòu)造函數(shù)調(diào)用會生成.prototype和.constructor引用)

ployfill

if (!Object.create) {
  Object.create = function (obj) {
    function Foo() {}
    Foo.prototype = obj
    return new Foo()
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市那槽,隨后出現(xiàn)的幾起案子悼沿,更是在濱河造成了極大的恐慌,老刑警劉巖骚灸,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糟趾,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)义郑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門蝶柿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人非驮,你說我怎么就攤上這事交汤。” “怎么了劫笙?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵芙扎,是天一觀的道長。 經(jīng)常有香客問我填大,道長戒洼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任允华,我火速辦了婚禮圈浇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘例获。我一直安慰自己汉额,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布榨汤。 她就那樣靜靜地躺著蠕搜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪收壕。 梳的紋絲不亂的頭發(fā)上妓灌,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機(jī)與錄音蜜宪,去河邊找鬼虫埂。 笑死,一個胖子當(dāng)著我的面吹牛圃验,可吹牛的內(nèi)容都是我干的掉伏。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼澳窑,長吁一口氣:“原來是場噩夢啊……” “哼斧散!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摊聋,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤鸡捐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后麻裁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箍镜,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡源祈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了色迂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片香缺。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖脚草,靈堂內(nèi)的尸體忽然破棺而出赫悄,到底是詐尸還是另有隱情,我是刑警寧澤馏慨,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站姑隅,受9級特大地震影響写隶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜讲仰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一慕趴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鄙陡,春花似錦冕房、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毫捣,卻和暖如春详拙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔓同。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工饶辙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斑粱。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓弃揽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親则北。 傳聞我的和親對象是個殘疾皇子矿微,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348