es6、7、8、9新語法新特性-總結

穆笙發表於2019-02-18

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物件上。現階段,某些方法同時在ObjectReflect物件上部署,未來的新方法將只部署在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方法,都會返回資料結構的當前成員的資訊。具體來說,就是返回一個包含valuedone兩個屬性的物件。其中,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方法,就會返回一個有著valuedone兩個屬性的物件。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)更好的語義。asyncawait,比起星號和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)

ES5不支援原生的模組化,在ES6中模組作為重要的組成部分被新增進來。模組的功能主要由 export 和 import 組成。每一個模組都有自己單獨的作用域,模組之間的相互呼叫關係是通過 export 來規定模組對外暴露的介面,通過import來引用其它模組提供的介面。同時還為模組創造了名稱空間,防止函式的命名衝突。

匯出(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}` }
}複製程式碼

參考

ECMAScript® 2016 語言標準

ECMAScript® 2017


相關文章