摘要:的類使用熟悉的關(guān)鍵字指定類繼承的函數(shù),并且可以通過方法訪問父類的構(gòu)造函數(shù)。例如繼承一個(gè)的類繼承了,術(shù)語上稱為基類,為派生類。例如注意到上例中,不僅是派生類的實(shí)例,也是派生類的實(shí)例,內(nèi)建對象繼承的實(shí)用之處是改變返回對象的類型。
和其它面向?qū)ο缶幊陶Z言一樣,ES6 正式定義了 class 類以及 extend 繼承語法糖,并且支持靜態(tài)、派生、抽象、迭代、單例等,而且根據(jù) ES6 的新特性衍生出很多有趣的用法。一、類的基本定義
基本所有面向?qū)ο蟮恼Z言都支持類的封裝與繼承,那什么是類?
類是面向?qū)ο蟪绦蛟O(shè)計(jì)的基礎(chǔ),包含數(shù)據(jù)封裝、數(shù)據(jù)操作以及傳遞消息的函數(shù)。類的實(shí)例稱為對象。
ES5 之前通過函數(shù)來模擬類的實(shí)現(xiàn)如下:
// 構(gòu)造函數(shù) function Person(name) { this.name = name; } // 原型上的方法 Person.prototype.sayName = function(){ console.log(this.name); }; // new 一個(gè)實(shí)例 var friend = new Person("Jenny"); friend.sayName(); // Jenny console.log(friend instanceof Person); // true console.log(friend instanceof Object); // true
總結(jié)來說,定義一個(gè)類的思路如下:
1.需要構(gòu)造函數(shù)封裝數(shù)據(jù)
2.在原型上添加方法操作數(shù)據(jù),
3.通過New創(chuàng)建實(shí)例
ES6 使用class關(guān)鍵字定義一個(gè)類,這個(gè)類有特殊的方法名[[Construct]]定義構(gòu)造函數(shù),在 new 創(chuàng)建實(shí)例時(shí)調(diào)用的就是[[Construct]],示例如下:
/*ES6*/ // 等價(jià)于 let Person = class { class Person { // 構(gòu)造函數(shù) constructor(name) { this.name = name; } // 等價(jià)于Person.prototype.sayName sayName() { console.log(this.name); } } console.log(typeof Person); // function console.log(typeof Person.prototype.sayName); // function let friend = new Person("Jenny"); friend.sayName(); // Jenny console.log(friend instanceof Person); // true console.log(friend instanceof Object); // true
上面的例子中class定義的類與自定義的函數(shù)模擬類功能上貌似沒什么不同,但本質(zhì)上還有很大差異的:
函數(shù)聲明可以被提升,但是class類聲明與let類似,不能被提升;
類聲明自動(dòng)運(yùn)行在嚴(yán)格模式下,“use strict”;
類中所有方法都是不可枚舉的,enumerable 為 false。
二、更靈活的類類和函數(shù)一樣,是JavaScript的一等公民(可以傳入函數(shù)、從函數(shù)返回、賦值),并且注意到類與對象字面量還有更多相似之處,這些特點(diǎn)可以擴(kuò)展出類更靈活的定義與使用。
2.1 擁有訪問器屬性對象的屬性有數(shù)據(jù)屬性和訪問屬性,類中也可以通過get、set關(guān)鍵字定義訪問器屬性:
class Person { constructor(name) { this.name = name; } get value () { return this.name + this.age } set value (num) { this.age = num } } let friend = new Person("Jenny"); // 調(diào)用的是 setter friend.value = 18 // 調(diào)用的是 getter console.log(friend.value) // Jenny182.2 可計(jì)算的成員名稱
類似 ES6 對象字面量擴(kuò)展的可計(jì)算屬性名稱,類也可以用[表達(dá)式]定義可計(jì)算成員名稱,包括類中的方法和訪問器屬性:
let methodName = "sayName" class Person { constructor(name) { this.name = name; } [methodName + "Default"]() { console.log(this.name); } get [methodName]() { return this.name } set [methodName](str) { this.name = str } } let friend = new Person("Jenny"); // 方法 friend.sayNameDefault(); // Jenny // 訪問器屬性 friend.sayName = "lee" console.log(friend.sayName) // lee
想進(jìn)一步熟悉對象新特性可參考:【ES6】對象的新功能與解構(gòu)賦值2.3 定義默認(rèn)迭代器
ES6 中常用的集合對象(數(shù)組、Set/Map集合)和字符串都是可迭代對象,如果類是用來表示值這些可迭代對象的,那么定義一個(gè)默認(rèn)迭代器會(huì)更有用。
ES6 通過給Symbol.iterator屬性添加生成器的方式,定義默認(rèn)迭代器:
class Person { constructor(name) { this.name = name; } *[Symbol.iterator]() { for (let item of this.name){ yield item } } } var abbrName = new Person(new Set(["j", "j", "e", "e", "n", "y", "y", "y",])) for (let x of abbrName) { console.log(x); // j e n y } console.log(...abbrName) // j e n y
定義默認(rèn)迭代器后類的實(shí)例就可以使用for-of循環(huán)和展開運(yùn)算符(...)等迭代功能。
對以上迭代器內(nèi)容感到困惑的可參考:【ES6】迭代器與可迭代對象2.4 作為參數(shù)的類
類作為"一等公民”可以當(dāng)參數(shù)使用傳入函數(shù)中,當(dāng)然也可以從函數(shù)中返回:
function createClass(className, val) { return new className(val) } let person = createClass(Person,"Jenny") console.log(person) // Person { name: "Jenny" } console.log(typeof person) // object2.5 創(chuàng)建單例
使用類語法創(chuàng)建單例的方式通過new立即調(diào)用類表達(dá)式:
let singleton = new class { constructor(name) { this.name = name; } }("Jenny") console.log(singleton.name) // Jenny
這里先創(chuàng)建匿名類表達(dá)式,然后 new 調(diào)用這個(gè)類表達(dá)式,并通過小括號立即執(zhí)行,這種類語法創(chuàng)建的單例不會(huì)在作用域中暴露類的引用。
三、類的繼承回顧 ES6 之前如何實(shí)現(xiàn)繼承?常用方式是通過原型鏈、構(gòu)造函數(shù)以及組合繼承等方式。
ES6 的類使用熟悉的extends關(guān)鍵字指定類繼承的函數(shù),并且可以通過surpe()方法訪問父類的構(gòu)造函數(shù)。
例如繼承一個(gè) Person 的類:
class Friend extends Person { constructor(name, phone){ super(name) this.phone = phone } } let myfriend = new Friend("lee",2233) console.log(myfriend) // Friend { name: "lee", phone: 2233 }
Friend 繼承了 Person,術(shù)語上稱 Person 為基類,F(xiàn)riend 為派生類。
需要注意的是,surpe()只能在派生類中使用,它負(fù)責(zé)初始化 this,所以派生類使用 this 之前一定要用surpe()。
3.1 繼承內(nèi)建對象ES6 的類繼承可以繼承內(nèi)建對象(Array、Set、Map 等),繼承后可以擁有基類的所有內(nèi)建功能。例如:
class MyArray extends Array { } let arr = new MyArray(1, 2, 3, 4), subarr = arr.slice(1, 3) console.log(arr.length) // 4 console.log(arr instanceof MyArray) // true console.log(arr instanceof Array) // true console.log(subarr instanceof MyArray) // true
注意到上例中,不僅 arr 是派生類 MyArray 的實(shí)例,subarr 也是派生類 MyArray 的實(shí)例,內(nèi)建對象繼承的實(shí)用之處是改變返回對象的類型。
瀏覽器引擎背后是通過[Symbol.species]屬性實(shí)現(xiàn)這一行為,它被用于返回函數(shù)的靜態(tài)訪問器屬性,內(nèi)建對象定義了[Symbol.species]屬性的有 Array、ArrayBuffer、Set、Map、Promise、RegExp、Typed arrays。3.2 繼承表達(dá)式的類
目前extends可以繼承類和內(nèi)建對象,但更強(qiáng)大的功能從表達(dá)式導(dǎo)出類!
這個(gè)表達(dá)式要求可以被解析為函數(shù)并具有[[Construct]]屬性和原型,示例如下:
function Sup(val) { this.value = val } Sup.prototype.getVal = function () { return "hello" + this.value } class Derived extends Sup { constructor(val) { super(val) } } let der = new Derived("world") console.log(der) // Derived { value: "world" } console.log(der.getVal()) // helloworld3.3 只能繼承的抽象類
ES6 引入new.target元屬性判斷函數(shù)是否通過new關(guān)鍵字調(diào)用。類的構(gòu)造函數(shù)也可以通過new.target確定類是如何被調(diào)用的。
可以通過new.target創(chuàng)建抽象類(不能實(shí)例化的類),例如:
class Abstract { constructor(){ if(new.target === Abstract) { throw new Error("抽象類(不能直接實(shí)例化)") } } } class Instantiable extends Abstract { constructor() { super() } } // let abs = new Abstract() // Error: 抽象類(不能直接實(shí)例化) let abs = new Instantiable() console.log(abs instanceof Abstract) // true
雖然不能直接使用 Abstract 抽象類創(chuàng)建實(shí)例,但是可以作為基類派生其它類。
四、類的靜態(tài)成員ES6 使用static關(guān)鍵字聲明靜態(tài)成員或方法。在類的方法或訪問器屬性前都可以使用static,唯一的限制是不能用于構(gòu)造函數(shù)。
靜態(tài)成員的作用是某些類成員的私有化,及不可在實(shí)例中訪問,必須要直接在類上訪問。
class Person { constructor(name) { this.name = name; } static create(name) { return new Person(name); } } let beauty = Person.create("Jenny"); // beauty.create("lee") // TypeError
如果基類有靜態(tài)成員,那這些靜態(tài)成員在派生類也可以使用。
例如將上例的 Person 作為基類,派生出 Friend 類并使用基類的靜態(tài)方法create( ):
class Friend extends Person { constructor(name){ super(name) } } var friend = Friend.create("lee") console.log(friend instanceof Person) // true console.log(friend instanceof Friend) // false
可以看出派生類依然可以使用基類的靜態(tài)方法。
推薦閱讀《深入理解ES6》
加油哦少年!文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/98979.html
摘要:創(chuàng)建自定義類型看下面一段代碼上面代碼使用創(chuàng)建了一個(gè)自定義類型,是這個(gè)類的構(gòu)造器,是類的公共方法。注意事項(xiàng)在使用類繼承的實(shí)現(xiàn)中,需要注意的點(diǎn)是如果子類沒有重寫方法,默認(rèn)會(huì)調(diào)用父類的構(gòu)造器方法。 es6 類-class 與大多正規(guī)的面向?qū)ο缶幊陶Z言不同(比如java),js在創(chuàng)建之初就不支持類。js的面向?qū)ο缶幊虒?shí)現(xiàn)方式是通過構(gòu)造函數(shù)和原型來實(shí)現(xiàn)的。 我之前以為es6引入類的概念將會(huì)帶給這...
摘要:接下來我們看下類的寫法,這個(gè)就很接近于傳統(tǒng)面向?qū)ο笳Z言了。如果你想了解傳統(tǒng)面向?qū)ο笳Z言,這里是一個(gè)好切入點(diǎn)。作為對象時(shí),指向父類的原型對象。這些就是為將來在中支持面向?qū)ο蟮念悪C(jī)制而預(yù)留的。 在ES5中,我們經(jīng)常使用方法或者對象去模擬類的使用,并基于原型實(shí)現(xiàn)繼承,雖然可以實(shí)現(xiàn)功能,但是代碼并不優(yōu)雅,很多人還是傾向于用 class 來組織代碼,很多類庫、框架創(chuàng)造了自己的 API 來實(shí)現(xiàn) c...
摘要:因?yàn)椴僮鞣麆?chuàng)建的對象都繼承自構(gòu)造函數(shù)的屬性。繼承的實(shí)現(xiàn)中常用的繼承方式是組合繼承,也就是通過構(gòu)造函數(shù)和原型鏈繼承同時(shí)來模擬繼承的實(shí)現(xiàn)。 原文發(fā)布在我的博客 我們都知道 JavaScript 是一門基于原型的語言。當(dāng)我們調(diào)用一個(gè)對象本身沒有的屬性時(shí),JavaScript 就會(huì)從對象的原型對象上去找該屬性,如果原型上也沒有該屬性,那就去找原型的原型,一直找原型鏈的末端也就是 Object....
摘要:生成的類的原型會(huì)被自動(dòng)調(diào)整,而你還能調(diào)用方法來訪問基類的構(gòu)造器。唯一能避免調(diào)用的辦法,是從類構(gòu)造器中返回一個(gè)對象。 起源 JS 從創(chuàng)建之初就不支持類,也沒有把類繼承作為定義相似對象以及關(guān)聯(lián)對象的主要方式,這讓不少開發(fā)者感到困惑。而從 ES1 誕生之前直到ES5 時(shí)期,很多庫都創(chuàng)建了一些工具,讓 JS 顯得貌似能支持類。盡管一些 JS 開發(fā)者強(qiáng)烈認(rèn)為這門語言不需要類,但為處理類而創(chuàng)建的代...
閱讀 1323·2021-11-25 09:43
閱讀 2038·2021-11-11 10:58
閱讀 1384·2021-11-08 13:18
閱讀 2833·2019-08-29 16:25
閱讀 3592·2019-08-29 12:51
閱讀 3399·2019-08-29 12:30
閱讀 819·2019-08-26 13:24
閱讀 3762·2019-08-26 10:38