1、let和const命令
let
命令,用來宣告變數。它的用法類似於var
,但是所宣告的變數,只在let
命令所在的程式碼塊內有效。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1複製程式碼
const
宣告一個只讀的常量。一旦宣告,常量的值就不能改變。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.複製程式碼
2、變數的解構賦值
基本用法
ES6允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構(Destructuring)。
本質上,這種寫法屬於“模式匹配”,只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值。下面是一些使用巢狀陣列進行解構的例子。
(1)陣列的解構賦值
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []複製程式碼
(2)物件的解構賦值
物件的解構與陣列有一個重要的不同。陣列的元素是按次序排列的,變數的取值由它的位置決定;而物件的屬性沒有次序,變數必須與屬性同名,才能取到正確的值。
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined複製程式碼
(3)字串的解構賦值
字串也可以解構賦值。這是因為此時,字串被轉換成了一個類似陣列的物件
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"複製程式碼
(4)數值和布林值的解構賦值
解構賦值時,如果等號右邊是數值和布林值,則會先轉為物件。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true複製程式碼
(5)函式引數的解構賦值
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]複製程式碼
function add([x, y]){
return x + y;
}
add([1, 2]); // 3複製程式碼
3、字串的擴充套件
API:
- codePointAt():能夠正確處理4個位元組儲存的字元,返回一個字元的碼點
- String.fromCodePoint():用於從碼點返回對應字元,但是這個方法不能識別32位的UTF-16字元
- at():返回字串給定位置的字元。該方法不能識別碼點大於
0xFFFF
的字 - normalize():用來將字元的不同表示方法統一為同樣的形式,這稱為Unicode正規化。
- includes():返回布林值,表示是否找到了引數字串。
- startsWith():返回布林值,表示引數字串是否在源字串的頭部
- endsWith():返回布林值,表示引數字串是否在源字串的尾部。
- repeat():返回一個新字串,表示將原字串重複
n
次。 - padStart()padEnd():如果某個字串不夠指定長度,會在頭部或尾部補全。
padStart
用於頭部補全,padEnd
用於尾部補全。
(1)字元的Unicode表示法
只要將碼點放入大括號,就能正確解讀該字元。
"\u{20BB7}"
// "?"
"\u{41}\u{42}\u{43}"
// "ABC"
let hello = 123;
hell\u{6F} // 123
'\u{1F680}' === '\uD83D\uDE80'
// true複製程式碼
(2)字串的遍歷器介面
ES6為字串新增了遍歷器介面,使得字串可以被for...of
迴圈遍歷。
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
複製程式碼
(3)模板字串
例項程式碼如下,一看便明瞭:
// 普通字串
`In JavaScript '\n' is a line-feed.`
// 多行字串
`In JavaScript this is
not legal.`
console.log(`string text line 1
string text line 2`);
// 字串中嵌入變數
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`複製程式碼
4、正則的擴充套件
(1)RegExp建構函式
RegExp建構函式第一個引數是一個正則物件,那麼可以使用第二個引數指定修飾符。而且,返回的正規表示式會忽略原有的正規表示式的修飾符,只使用新指定的修飾符。
new RegExp(/abc/ig, 'i').flags
// "i"複製程式碼
(2)字串的正則方法
字串物件共有4個方法,可以使用正規表示式:match()
、replace()
、search()
和split()
。
ES6將這4個方法,在語言內部全部呼叫RegExp的例項方法,從而做到所有與正則相關的方法,全都定義在RegExp物件上。
String.prototype.match
呼叫RegExp.prototype[Symbol.match]
String.prototype.replace
呼叫RegExp.prototype[Symbol.replace]
String.prototype.search
呼叫RegExp.prototype[Symbol.search]
String.prototype.split
呼叫RegExp.prototype[Symbol.split]
(3)u修飾符
- 點字元
- Unicode字元表示法
- 量詞
- 預定義模式
- i修飾符
5、ES6 數值的擴充套件
二進位制和八進位制表示法
ES6提供了二進位制和八進位制數值的新的寫法,分別用字首0b
(或0B
)和0o
(或0O
)表示。
API
- Number.isFinite()
- Number.isNaN()
- Number.parseInt()
- Number.parseFloat()
- Number.isInteger()
- Number.EPSILON
6、陣列的擴充套件
Array.from()
用於將兩類物件轉為真正的陣列:類似陣列的物件(array-like object)和可遍歷(iterable)的物件(包括ES6新增的資料結構Set和Map)
Array.of()
Array.of
方法用於將一組值,轉換為陣列。
7、函式的擴充套件
ES6允許為函式的引數設定預設值,即直接寫在引數定義的後面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello複製程式碼
(1)箭頭函式
ES6允許使用“箭頭”(=>
)定義函式。
var f = v => v;
// 等於
var f = function(v) {
return v;
};
複製程式碼
(2)擴充套件運算子
擴充套件運算子(spread)是三個點(...)。它好比 rest 引數的逆運算,將一個陣列轉為用逗號分隔的引數序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]複製程式碼
8、物件的擴充套件
(1)屬性和方法的簡潔表示法
ES6允許直接寫入變數和函式,作為物件的屬性和方法。這樣的書寫更加簡潔。
var foo = 'bar';
var baz = {foo};
baz // {foo: "bar"}
// 等同於
var baz = {foo: foo};複製程式碼
(2)方法的簡潔表示
var o = {
method() {
return "Hello!";
}
};
// 等同於
var o = {
method: function() {
return "Hello!";
}
};複製程式碼
(3)Object.assign
Object.assign方法用於物件的合併,將源物件(source)的所有可列舉屬性,複製到目標物件(target)。
var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}複製程式碼
Object.assign方法實行的是淺拷貝,而不是深拷貝。也就是說,如果源物件某個屬性的值是物件,那麼目標物件拷貝得到的是這個物件的引用。
9、Symbol
Symbol值通過Symbol函式生成。這就是說,物件的屬性名現在可以有兩種型別,一種是原來就有的字串,另一種就是新增的Symbol型別。凡是屬性名屬於Symbol型別,就都是獨一無二的,可以保證不會與其他屬性名產生衝突。
10、Promise物件
Promise 是非同步程式設計的一種解決方案,比傳統的解決方案——回撥函式和事件——更合理和更強大。它由社群最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供了Promise物件。
所謂Promise,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise 是一個物件,從它可以獲取非同步操作的訊息。Promise 提供統一的 API,各種非同步操作都可以用同樣的方法進行處理。
Promise物件有以下兩個特點。
(1)物件的狀態不受外界影響。Promise物件代表一個非同步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise物件的狀態改變,只有兩種可能:從Pending變為Resolved和從Pending變為Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。如果改變已經發生了,你再對Promise物件新增回撥函式,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的處。
基本用法
ES6規定,Promise物件是一個建構函式,用來生成Promise例項。
下面程式碼創造了一個Promise例項。
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 非同步操作成功 */){
resolve(value);
} else {
reject(error);
}
});複製程式碼
Promise例項生成以後,可以用then方法分別指定Resolved狀態和Reject狀態的回撥函式。
promise.then(function(value) {
// success
}, function(error) {
// failure
});複製程式碼
下面是非同步載入圖片的例子。
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
var image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}複製程式碼
11、set和map資料結構
Set基本用法
S6提供了新的資料結構Set。它類似於陣列,但是成員的值都是唯一的,沒有重複的值。
Set本身是一個建構函式,用來生成Set資料結構
var s = new Set();
[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4複製程式碼
Map結構的目的和基本用法
ES6提供了Map資料結構。它類似於物件,也是鍵值對的集合,但是“鍵”的範圍不限於字串,各種型別的值(包括物件)都可以當作鍵。也就是說,Object結構提供了“字串—值”的對應,Map結構提供了“值—值”的對應,是一種更完善的Hash結構實現。如果你需要“鍵值對”的資料結構,Map比Object更合適。
var m = new Map();
var o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false複製程式碼
12、Proxy 和 Reflect
Proxy
Proxy 用於修改某些操作的預設行為,等同於在語言層面做出修改,所以屬於一種“超程式設計”(meta programming),即對程式語言進行程式設計。
Proxy 可以理解成,在目標物件之前架設一層“攔截”,外界對該物件的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這裡表示由它來“代理”某些操作,可以譯為“代理器”。
Proxy
Reflect
物件與Proxy
物件一樣,也是ES6為了操作物件而提供的新API。Reflect
物件的設計目的有這樣幾個。
(1) 將Object
物件的一些明顯屬於語言內部的方法(比如Object.defineProperty
),放到Reflect
物件上。現階段,某些方法同時在Object
和Reflect
物件上部署,未來的新方法將只部署在Reflect
物件上。
(2) 修改某些Object方法的返回結果,讓其變得更合理。比如,Object.defineProperty(obj, name, desc)
在無法定義屬性時,會丟擲一個錯誤,而Reflect.defineProperty(obj, name, desc)
則會返回false
。
// 老寫法
try {
Object.defineProperty(target, property, attributes);
// success
} catch (e) {
// failure
}
// 新寫法
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}複製程式碼
13、Iterator和for...of迴圈
terator(遍歷器)的概念
JavaScript原有的表示“集合”的資料結構,主要是陣列(Array)和物件(Object),ES6又新增了Map和Set。這樣就有了四種資料集合,使用者還可以組合使用它們,定義自己的資料結構,比如陣列的成員是Map,Map的成員是物件。這樣就需要一種統一的介面機制,來處理所有不同的資料結構。
遍歷器(Iterator)就是這樣一種機制。它是一種介面,為各種不同的資料結構提供統一的訪問機制。任何資料結構只要部署Iterator介面,就可以完成遍歷操作(即依次處理該資料結構的所有成員)。
Iterator的作用有三個:一是為各種資料結構,提供一個統一的、簡便的訪問介面;二是使得資料結構的成員能夠按某種次序排列;三是ES6創造了一種新的遍歷命令for...of
迴圈,Iterator介面主要供for...of
消費。
Iterator的遍歷過程是這樣的。
(1)建立一個指標物件,指向當前資料結構的起始位置。也就是說,遍歷器物件本質上,就是一個指標物件。
(2)第一次呼叫指標物件的next
方法,可以將指標指向資料結構的第一個成員。
(3)第二次呼叫指標物件的next
方法,指標就指向資料結構的第二個成員。
(4)不斷呼叫指標物件的next
方法,直到它指向資料結構的結束位置。
每一次呼叫next
方法,都會返回資料結構的當前成員的資訊。具體來說,就是返回一個包含value
和done
兩個屬性的物件。其中,value
屬性是當前成員的值,done
屬性是一個布林值,表示遍歷是否結束。
下面是一個模擬next
方法返回值的例子。
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}複製程式碼
for...of迴圈
ES6 借鑑 C++、Java、C# 和 Python 語言,引入了for...of
迴圈,作為遍歷所有資料結構的統一的方法。
一個資料結構只要部署了Symbol.iterator
屬性,就被視為具有iterator介面,就可以用for...of
迴圈遍歷它的成員。也就是說,for...of
迴圈內部呼叫的是資料結構的Symbol.iterator
方法。
for...of
迴圈可以使用的範圍包括陣列、Set 和 Map 結構、某些類似陣列的物件(比如arguments
物件、DOM NodeList 物件)、後文的 Generator 物件,以及字串。
陣列原生具備iterator
介面(即預設部署了Symbol.iterator
屬性),for...of
迴圈本質上就是呼叫這個介面產生的遍歷器,可以用下面的程式碼證明。
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);
for(let v of obj) {
console.log(v); // red green blue
}複製程式碼
14、Generator/yield
Generator函式是ES6提供的一種非同步程式設計解決方案,語法行為與傳統函式完全不同
Generator函式有多種理解角度。從語法上,首先可以把它理解成,Generator函式是一個狀態機,封裝了多個內部狀態。
執行Generator函式會返回一個遍歷器物件,也就是說,Generator函式除了狀態機,還是一個遍歷器物件生成函式。返回的遍歷器物件,可以依次遍歷Generator函式內部的每一個狀態。
形式上,Generator函式是一個普通函式,但是有兩個特徵。一是,function
關鍵字與函式名之間有一個星號;二是,函式體內部使用yield
語句,定義不同的內部狀態(yield語句在英語裡的意思就是“產出”)。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();複製程式碼
上面程式碼定義了一個Generator函式helloWorldGenerator
,它內部有兩個yield
語句“hello”和“world”,即該函式有三個狀態:hello,world和return語句(結束執行)。
然後,Generator函式的呼叫方法與普通函式一樣,也是在函式名後面加上一對圓括號。不同的是,呼叫Generator函式後,該函式並不執行,返回的也不是函式執行結果,而是一個指向內部狀態的指標物件,也就是上一章介紹的遍歷器物件(Iterator Object)。
下一步,必須呼叫遍歷器物件的next方法,使得指標移向下一個狀態。也就是說,每次呼叫next
方法,內部指標就從函式頭部或上一次停下來的地方開始執行,直到遇到下一個yield
語句(或return
語句)為止。換言之,Generator函式是分段執行的,yield
語句是暫停執行的標記,而next
方法可以恢復執行。
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }複製程式碼
上面程式碼一共呼叫了四次next
方法。
第一次呼叫,Generator函式開始執行,直到遇到第一個yield
語句為止。next
方法返回一個物件,它的value
屬性就是當前yield
語句的值hello,done
屬性的值false,表示遍歷還沒有結束。
第二次呼叫,Generator函式從上次yield
語句停下的地方,一直執行到下一個yield
語句。next
方法返回的物件的value
屬性就是當前yield
語句的值world,done
屬性的值false,表示遍歷還沒有結束。
第三次呼叫,Generator函式從上次yield
語句停下的地方,一直執行到return
語句(如果沒有return語句,就執行到函式結束)。next
方法返回的物件的value
屬性,就是緊跟在return
語句後面的表示式的值(如果沒有return
語句,則value
屬性的值為undefined),done
屬性的值true,表示遍歷已經結束。
第四次呼叫,此時Generator函式已經執行完畢,next
方法返回物件的value
屬性為undefined,done
屬性為true。以後再呼叫next
方法,返回的都是這個值。
總結一下,呼叫Generator函式,返回一個遍歷器物件,代表Generator函式的內部指標。以後,每次呼叫遍歷器物件的next
方法,就會返回一個有著value
和done
兩個屬性的物件。value
屬性表示當前的內部狀態的值,是yield
語句後面那個表示式的值;done
屬性是一個布林值,表示是否遍歷結束
yield語句
由於Generator函式返回的遍歷器物件,只有呼叫next
方法才會遍歷下一個內部狀態,所以其實提供了一種可以暫停執行的函式。yield
語句就是暫停標誌。
遍歷器物件的next
方法的執行邏輯如下。
(1)遇到yield
語句,就暫停執行後面的操作,並將緊跟在yield
後面的那個表示式的值,作為返回的物件的value
屬性值。
(2)下一次呼叫next
方法時,再繼續往下執行,直到遇到下一個yield
語句。
(3)如果沒有再遇到新的yield
語句,就一直執行到函式結束,直到return
語句為止,並將return
語句後面的表示式的值,作為返回的物件的value
屬性值。
(4)如果該函式沒有return
語句,則返回的物件的value
屬性值為undefined
。
需要注意的是,yield
語句後面的表示式,只有當呼叫next
方法、內部指標指向該語句時才會執行,因此等於為JavaScript提供了手動的“惰性求值”(Lazy Evaluation)的語法功能。
function* gen() {
yield 123 + 456;
}複製程式碼
上面程式碼中,yield後面的表示式123 + 456
,不會立即求值,只會在next
方法將指標移到這一句時,才會求值。
yield
語句與return
語句既有相似之處,也有區別。相似之處在於,都能返回緊跟在語句後面的那個表示式的值。區別在於每次遇到yield
,函式暫停執行,下一次再從該位置繼續向後執行,而return
語句不具備位置記憶的功能。一個函式裡面,只能執行一次(或者說一個)return
語句,但是可以執行多次(或者說多個)yield
語句。正常函式只能返回一個值,因為只能執行一次return
;Generator函式可以返回一系列的值,因為可以有任意多個yield
。從另一個角度看,也可以說Generator生成了一系列的值,這也就是它的名稱的來歷(在英語中,generator這個詞是“生成器”的意思)
15、async與await
ES7提供了async
函式,使得非同步操作變得更加方便。async
函式是什麼?一句話,async
函式就是Generator函式的語法糖。
依次讀取兩個檔案。
var fs = require('fs');
var readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) reject(error);
resolve(data);
});
});
};
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};複製程式碼
寫成async
函式,就是下面這樣
var asyncReadFile = async function (){
var f1 = await readFile('/etc/fstab');
var f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
}複製程式碼
一比較就會發現,async
函式就是將Generator函式的星號(*
)替換成async
,將yield
替換成await
,僅此而已。
async
函式對 Generator 函式的改進,體現在以下四點
(1)內建執行器。Generator函式的執行必須靠執行器,所以才有了co
模組,而async
函式自帶執行器。也就是說,async
函式的執行,與普通函式一模一樣,只要一行。
(2)更好的語義。async
和await
,比起星號和yield
,語義更清楚了。async
表示函式裡有非同步操作,await
表示緊跟在後面的表示式需要等待結果。
(3)更廣的適用性。 co
模組約定,yield
命令後面只能是Thunk函式或Promise物件,而async
函式的await
命令後面,可以是Promise物件和原始型別的值(數值、字串和布林值,但這時等同於同步操作)。
(4)返回值是Promise。async
函式的返回值是Promise物件,這比Generator函式的返回值是Iterator物件方便多了。你可以用then
方法指定下一步的操作。
進一步說,async
函式完全可以看作多個非同步操作,包裝成的一個Promise物件,而await
命令就是內部then
命令的語法糖。
16、Class
對熟悉Java,object-c,c#等純面嚮物件語言的開發者來說,都會對class有一種特殊的情懷。ES6 引入了class(類),讓JavaScript的物件導向程式設計變得更加簡單和易於理解。
class Animal {
// 建構函式,例項化的時候將會被呼叫,如果不指定,那麼會有一個不帶引數的預設建構函式.
constructor(name,color) {
this.name = name;
this.color = color;
}
// toString 是原型物件上的屬性
toString() {
console.log('name:' + this.name + ',color:' + this.color);
}
}
var animal = new Animal('dog','white');//例項化Animal
animal.toString();
console.log(animal.hasOwnProperty('name')); //true
console.log(animal.hasOwnProperty('toString')); // false
console.log(animal.__proto__.hasOwnProperty('toString')); // true
class Cat extends Animal {
constructor(action) {
// 子類必須要在constructor中指定super 函式,否則在新建例項的時候會報錯.
// 如果沒有置頂consructor,預設帶super函式的constructor將會被新增、
super('cat','white');
this.action = action;
}
toString() {
console.log(super.toString());
}
}
var cat = new Cat('catch')
cat.toString();
// 例項cat 是 Cat 和 Animal 的例項,和Es5完全一致。
console.log(cat instanceof Cat); // true
console.log(cat instanceof Animal); // true複製程式碼
17、模組化(Module)
匯出(export)
ES6允許在一個模組中使用export來匯出多個變數或函式。
匯出變數/常量
//test.js
export var name = 'Rainbow'
複製程式碼
ES6將一個檔案視為一個模組,上面的模組通過 export 向外輸出了一個變數。一個模組也可以同時往外面輸出多個變數。
//test.js
var name = 'Rainbow';
var age = '24';
export {name, age};
複製程式碼
匯出函式
// myModule.js
export function myModule(someArg) {
return someArg;
}
複製程式碼
匯入(import)
定義好模組的輸出以後就可以在另外一個模組通過import引用。
import {myModule} from 'myModule';// main.js
import {name,age} from 'test';// test.js
複製程式碼
18、修飾器(Decorator)
修飾器(Decorator)是一個函式,用來修改類的行為。這是ES7的一個提案,目前Babel轉碼器已經支援。
修飾器對類的行為的改變,是程式碼編譯時發生的,而不是在執行時。這意味著,修飾器能在編譯階段執行程式碼。
類的修飾
function testable(target) {
target.isTestable = true;
}
@testable
class MyTestableClass {}
console.log(MyTestableClass.isTestable) // true複製程式碼
上面程式碼中,@testable
就是一個修飾器。它修改了MyTestableClass
這個類的行為,為它加上了靜態屬性isTestable
。
基本上,修飾器的行為就是下面這樣。
@decorator
class A {}
// 等同於
class A {}
A = decorator(A) || A;複製程式碼
方法的修飾
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}複製程式碼