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