摘要:在規(guī)范中,引入了的概念。使用中的聲明一個(gè)類(lèi),是非常簡(jiǎn)單的事。中面向?qū)ο髮?shí)例化的背后原理,實(shí)際上就是原型對(duì)象。與區(qū)別理解上述原理后,還需要注意與屬性的區(qū)別。實(shí)際上,在中,類(lèi)繼承的本質(zhì)依舊是原型對(duì)象。
在 ES6 規(guī)范中,引入了 class 的概念。使得 JS 開(kāi)發(fā)者終于告別了,直接使用原型對(duì)象模仿面向?qū)ο笾械念?lèi)和類(lèi)繼承時(shí)代。
但是JS 中并沒(méi)有一個(gè)真正的 class 原始類(lèi)型, class 僅僅只是對(duì)原型對(duì)象運(yùn)用語(yǔ)法糖。所以,只有理解如何使用原型對(duì)象實(shí)現(xiàn)類(lèi)和類(lèi)繼承,才能真正地用好 class。
ES6:class通過(guò)類(lèi)來(lái)創(chuàng)建對(duì)象,使得開(kāi)發(fā)者不必寫(xiě)重復(fù)的代碼,以達(dá)到代碼復(fù)用的目的。它基于的邏輯是,兩個(gè)或多個(gè)對(duì)象的結(jié)構(gòu)功能類(lèi)似,可以抽象出一個(gè)模板,依照模板復(fù)制出多個(gè)相似的對(duì)象。就像自行車(chē)制造商一遍一遍地復(fù)用相同的藍(lán)圖來(lái)制造大量的自行車(chē)。
使用 ES6 中的 class 聲明一個(gè)類(lèi),是非常簡(jiǎn)單的事。它的語(yǔ)法如下:
class Person { constructor(name){ this.name = name } hello(){ console.log("Hello, my name is " + this.name + "."); } } var xiaoMing = new Person("xiaoMing"); xiaoMing.hello() // Hello, my name is xiaoMing.
xiaoMing 是通過(guò)類(lèi) Person 實(shí)例化出來(lái)的對(duì)象。對(duì)象 xiaoMing 是按照類(lèi) Person 這個(gè)模板,實(shí)例化出來(lái)的對(duì)象。實(shí)例化出來(lái)的對(duì)象擁有類(lèi)預(yù)先訂制好的結(jié)構(gòu)和功能。
ES6 的語(yǔ)法很簡(jiǎn)單,但是在實(shí)例化的背后,究竟是什么在起作用呢?
class 實(shí)例化的背后原理使用 class 的語(yǔ)法,讓開(kāi)發(fā)者告別了使用 prototype 模仿面向?qū)ο蟮臅r(shí)代。但是,class 并不是 ES6 引入的全新概念,它的原理依舊是原型繼承。
typeof class == "function"通過(guò)類(lèi)型判斷,我們可以得知,class 的并不是什么全新的數(shù)據(jù)類(lèi)型,它實(shí)際只是 function (或者說(shuō) object)。
class Person { // ... } typeof Person // function
為了更加直觀地了解 Person 的實(shí)質(zhì),可以將它在控制臺(tái)打印出來(lái),如下。
Person 的屬性并不多,除去用 [[...]] 包起來(lái)的內(nèi)置屬性外,大部分屬性根據(jù)名字就能明白它的作用。需要我們重點(diǎn)關(guān)注的是 prototype 和 __proto__ 兩個(gè)屬性。
(關(guān)于 __proto__ 可以在本文的姊妹篇 找到答案)
實(shí)例化的原理: prototype先來(lái)講講 prototype 屬性,它指向一個(gè)特殊性對(duì)象:原型對(duì)象。
原型對(duì)象所以特殊,是因?yàn)樗鼡碛幸粋€(gè)普通對(duì)象沒(méi)有的能力:將它的屬性共享給其他對(duì)象。
在 ES6 規(guī)范 中,對(duì) 原型對(duì)象 是如下定義的:
object that provides shared properties for other objects
原型對(duì)象是如何將它的屬性分享給其他對(duì)象的呢?
這里使用 ES5 創(chuàng)建一個(gè)類(lèi),并將它實(shí)例化,來(lái)看看它的實(shí)質(zhì)。
function Person() { this.name = name } // 1. 首先給 Person.prototype 原型對(duì)象添加了 describe 方法 。 Person.prototype.describe = function(){ console.log("Hello, my name is " + this.name + "."); } // 2. 實(shí)例化對(duì)象的 __proto__ 指向 Person.prototype var jane = new Person("jane"); jane.__proto__ === Person.prototype; // 3. 讀取 describe 方法時(shí),實(shí)際會(huì)沿著原型鏈查找到 Person.prototype 原型對(duì)象上。 jane.describe() // Hello, my name is jane.
上述使用 JS 模仿面向?qū)ο髮?shí)例化的背后,實(shí)際有三個(gè)步驟:
首先給 Person.prototype 屬性所指的原型對(duì)象上添加了一個(gè)方法 describe。
在使用 new 關(guān)鍵字創(chuàng)建對(duì)象時(shí),會(huì)默認(rèn)給該對(duì)象添加一個(gè)原型屬性 __proto__,該屬性指向 Person.prototype 原型對(duì)象。
在讀取 describe 方法時(shí),首先會(huì)在 jane 對(duì)象查找該方法,但是 jane 對(duì)象并不直接擁有 describe 方法。所以會(huì)沿著原型鏈查找到 Person.prototype 原型對(duì)象上,最后返回該原型對(duì)象的 describe 方法。
JS 中面向?qū)ο髮?shí)例化的背后原理,實(shí)際上就是 原型對(duì)象。
為了方便大家理解,從網(wǎng)上扒了一張的圖片,放到這來(lái)便于大家理解。
prototype 與 __proto__ 區(qū)別理解上述原理后,還需要注意 prototype 與 __proto__ 屬性的區(qū)別。
__proto__ 所指的對(duì)象,真正將它的屬性分享給它所屬的對(duì)象。所有的對(duì)象都有 __proto__ 屬性,它是一個(gè)內(nèi)置屬性,被用于繼承。
prototype 是一個(gè)只屬于 function 的屬性。當(dāng)使用 new 方法調(diào)用該構(gòu)造函數(shù)的時(shí)候,它被用于構(gòu)建新對(duì)象的 __proto__。另外它不可寫(xiě),不可枚舉,不可配置。
( new Foo() ).__proto__ === Foo.prototype ( new Foo() ).prototype === undefinedclass 定義屬性
當(dāng)我們使用 class 定義屬性(方法)的時(shí)候,實(shí)際上等于是在 class 的原型對(duì)象上定義屬性。
class Foo { constructor(){ /* constructor */ } describe(){ /* describe */ } } // 等價(jià)于 function Foo (){ /* constructor */ } Foo.prototype.describe = function(){ /* describe */ }
constructor 是一個(gè)比較特殊的屬性,它指向構(gòu)造函數(shù)(類(lèi))本身。可以通過(guò)以下代碼驗(yàn)證。
Foo.prototype.constructor === Foo // true類(lèi)繼承
在傳統(tǒng)面向?qū)ο笾校?lèi)是可以繼承類(lèi)的。這樣子類(lèi)就可以復(fù)制父類(lèi)的方法,達(dá)到代碼復(fù)用的目的。
ES6 也提供了類(lèi)繼承的語(yǔ)法 extends,如下:
class Foo { constructor(who){ this.me = who; } identify(){ return "I am " + this.me; } } class Bar extends Foo { constructor(who){ // super() 指的是調(diào)用父類(lèi) // 調(diào)用的同時(shí),會(huì)綁定 this 。 // 如:Foo.call(this, who) super(who); } speak(){ alert( "Hello, " + this.identify() + "." ); } } var b1 = new Bar( "b1" ); b1.speak();
當(dāng)實(shí)例 b1 調(diào)用 speak 方法時(shí),b1 本身沒(méi)有 speak,所以會(huì)到 Bar.prototype 原型對(duì)象上查找,并且調(diào)用原型對(duì)象上的 speak 方法。調(diào)用 identify 方式時(shí),由于 this 指向的是 b1 對(duì)象。所以也會(huì)先在 b1 本身查找,然后沿著原型鏈,查找 Bar.prototype,最后在 Foo.prototype 原型對(duì)象上找到 identify 方法,然后調(diào)用。
實(shí)際上,在 JavaScript 中,類(lèi)繼承的本質(zhì)依舊是原型對(duì)象。
他們的關(guān)系如下圖所示:
參考文章(ES6 規(guī)范)[http://www.ecma-international...
MDN Classes
You-Dont-Know-JS
JavaScript difference between proto and prototype
proto VS. prototype in JavaScript
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81502.html
摘要:新建一個(gè)類(lèi)該函數(shù)返回一個(gè)類(lèi)的實(shí)例給函數(shù)傳入通過(guò)立即調(diào)用類(lèi)構(gòu)造函數(shù)可以創(chuàng)建單例。派生類(lèi)是指繼承自其它類(lèi)的新類(lèi)。在構(gòu)造函數(shù)中訪(fǎng)問(wèn)之前要調(diào)用,負(fù)責(zé)初始化。在構(gòu)造函數(shù)中使用通常表示當(dāng)前的構(gòu)造函數(shù)名。 ES5中的近類(lèi)結(jié)構(gòu) ES5以及之前的版本,沒(méi)有類(lèi)的概念,但是聰明的JavaScript開(kāi)發(fā)者,為了實(shí)現(xiàn)面向?qū)ο螅瑒?chuàng)建了特殊的近類(lèi)結(jié)構(gòu)。 ES5中創(chuàng)建類(lèi)的方法:新建一個(gè)構(gòu)造函數(shù),定義一個(gè)方法并且賦值...
摘要:新建一個(gè)類(lèi)該函數(shù)返回一個(gè)類(lèi)的實(shí)例給函數(shù)傳入通過(guò)立即調(diào)用類(lèi)構(gòu)造函數(shù)可以創(chuàng)建單例。派生類(lèi)是指繼承自其它類(lèi)的新類(lèi)。在構(gòu)造函數(shù)中訪(fǎng)問(wèn)之前要調(diào)用,負(fù)責(zé)初始化。在構(gòu)造函數(shù)中使用通常表示當(dāng)前的構(gòu)造函數(shù)名。 ES5中的近類(lèi)結(jié)構(gòu) ES5以及之前的版本,沒(méi)有類(lèi)的概念,但是聰明的JavaScript開(kāi)發(fā)者,為了實(shí)現(xiàn)面向?qū)ο螅瑒?chuàng)建了特殊的近類(lèi)結(jié)構(gòu)。 ES5中創(chuàng)建類(lèi)的方法:新建一個(gè)構(gòu)造函數(shù),定義一個(gè)方法并且賦值...
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書(shū)了入門(mén),覺(jué)得看看這本書(shū)就足夠了。前端的異步解決方案之和異步編程模式在前端開(kāi)發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(shū)(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書(shū)的目的是以目前還在制定中的ECMASc...
摘要:下面輪到我們的主角原型繼承登場(chǎng)了,它從另一個(gè)角度解決了重用的問(wèn)題。原型繼承的原理原型對(duì)象中的由兩部分組成,普通屬性的集合,和原型屬性。原型繼承的實(shí)現(xiàn)在上面的例子中,通過(guò)直接修改了屬性值,實(shí)現(xiàn)了原型繼承。使用原型繼承,同樣可以達(dá)到重用的目的。 繼承的本質(zhì):重用 在探討 JavaScript 的原型繼承之前,先不妨想想為什么要繼承? 考慮一個(gè)場(chǎng)景,如果我們有兩個(gè)對(duì)象,它們一部分屬性相同,另...
閱讀 2596·2023-04-25 15:07
閱讀 705·2021-11-24 10:21
閱讀 2298·2021-09-22 10:02
閱讀 3517·2019-08-30 15:43
閱讀 3222·2019-08-30 13:03
閱讀 2287·2019-08-29 17:18
閱讀 3586·2019-08-29 17:07
閱讀 1873·2019-08-29 12:27