ES6 更易于繼承的類語法的使用
和其它面向?qū)ο缶幊陶Z言一樣,ES6 正式定義了 class 類以及 extend 繼承語法糖,并且支持靜態(tài)、派生、抽象、迭代、單例等,而且根據(jù) ES6 的新特性衍生出很多有趣的用法。
一、類的基本定義
基本所有面向?qū)ο蟮恼Z言都支持類的封裝與繼承,那什么是類?
類是面向?qū)ο蟪绦蛟O(shè)計的基礎(chǔ),包含數(shù)據(jù)封裝、數(shù)據(jù)操作以及傳遞消息的函數(shù)。類的實例稱為對象。
ES5 之前通過函數(shù)來模擬類的實現(xiàn)如下:
// 構(gòu)造函數(shù)
function Person(name) {
this.name = name;
}
// 原型上的方法
Person.prototype.sayName = function(){
console.log(this.name);
};
// new 一個實例
var friend = new Person("Jenny");
friend.sayName(); // Jenny
console.log(friend instanceof Person); // true
console.log(friend instanceof Object); // true
總結(jié)來說,定義一個類的思路如下:
1.需要構(gòu)造函數(shù)封裝數(shù)據(jù)
2.在原型上添加方法操作數(shù)據(jù),
3.通過New創(chuàng)建實例
ES6 使用class關(guān)鍵字定義一個類,這個類有特殊的方法名[[Construct]]定義構(gòu)造函數(shù),在 new 創(chuàng)建實例時調(diào)用的就是[[Construct]],示例如下:
/*ES6*/
// 等價于 let Person = class {
class Person {
// 構(gòu)造函數(shù)
constructor(name) {
this.name = name;
}
// 等價于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類似,不能被提升;
- 類聲明自動運(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) // Jenny18
2.2 可計算的成員名稱
類似 ES6 對象字面量擴(kuò)展的可計算屬性名稱,類也可以用[表達(dá)式]定義可計算成員名稱,包括類中的方法和訪問器屬性:
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集合)和字符串都是可迭代對象,如果類是用來表示值這些可迭代對象的,那么定義一個默認(rèn)迭代器會更有用。
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)迭代器后類的實例就可以使用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) // object
2.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)用這個類表達(dá)式,并通過小括號立即執(zhí)行,這種類語法創(chuàng)建的單例不會在作用域中暴露類的引用。
三、類的繼承
回顧 ES6 之前如何實現(xiàn)繼承?常用方式是通過原型鏈、構(gòu)造函數(shù)以及組合繼承等方式。
ES6 的類使用熟悉的extends關(guān)鍵字指定類繼承的函數(shù),并且可以通過surpe()方法訪問父類的構(gòu)造函數(shù)。
例如繼承一個 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 的實例,subarr 也是派生類 MyArray 的實例,內(nèi)建對象繼承的實用之處是改變返回對象的類型。
瀏覽器引擎背后是通過[Symbol.species]屬性實現(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)出類!
這個表達(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()) // helloworld
3.3 只能繼承的抽象類
ES6 引入new.target元屬性判斷函數(shù)是否通過new關(guān)鍵字調(diào)用。類的構(gòu)造函數(shù)也可以通過new.target確定類是如何被調(diào)用的。
可以通過new.target創(chuàng)建抽象類(不能實例化的類),例如:
class Abstract {
constructor(){
if(new.target === Abstract) {
throw new Error('抽象類(不能直接實例化)')
}
}
}
class Instantiable extends Abstract {
constructor() {
super()
}
}
// let abs = new Abstract() // Error: 抽象類(不能直接實例化)
let abs = new Instantiable()
console.log(abs instanceof Abstract) // true雖然不能直接使用 Abstract 抽象類創(chuàng)建實例,但是可以作為基類派生其它類。
四、類的靜態(tài)成員
ES6 使用static關(guān)鍵字聲明靜態(tài)成員或方法。在類的方法或訪問器屬性前都可以使用static,唯一的限制是不能用于構(gòu)造函數(shù)。
靜態(tài)成員的作用是某些類成員的私有化,及不可在實例中訪問,必須要直接在類訪問。
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)方法。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
超出JavaScript安全整數(shù)限制的數(shù)字計算BigInt詳解
這篇文章給大家分享了超出JavaScript安全整數(shù)限制的數(shù)字計算BigInt的相關(guān)知識點(diǎn),有興趣的朋友參考學(xué)習(xí)下。2018-06-06
JavaScript跨瀏覽器獲取頁面中相同class節(jié)點(diǎn)的方法
這篇文章主要介紹了JavaScript跨瀏覽器獲取頁面中相同class節(jié)點(diǎn)的方法,本文講解使用getELementsByClassName函數(shù)解決這個需求,并給了一個開源的getELementsByClassName函數(shù)實現(xiàn),功能更加強(qiáng)大,需要的朋友可以參考下2015-03-03
javascript function調(diào)用時的參數(shù)檢測常用辦法
js中并不直接支持類似c#的方法重載,所以只能變相的來解決,示意代碼:(利用了內(nèi)置屬性arguments)2010-02-02

