本章節彙整了本書所有章節的概觀部分。
Math
功能
Number
屬性Math
方法let
const
...
)Object
中的新方法
for-of
迴圈
then()
呼叫ES6 規範的引言列出了所有新功能
[ECMAScript 6] 的一些主要強化功能包括模組、類別宣告、詞彙區塊範圍、迭代器和產生器、非同步程式設計的 Promise、解構模式和適當的尾部呼叫。ECMAScript 內建函式庫已擴充,支援額外的資料抽象,包括 Map、Set 和二進制數值陣列,以及在字串和正規表示式中額外支援 Unicode 補充字元。內建函式現在可透過子類別化進行擴充。
功能有三大類
Math
功能 現在可以使用二進制和八進制表示法指定整數
> 0xFF // ES5: hexadecimal
255
> 0b11 // ES6: binary
3
> 0o10 // ES6: octal
8
Number
屬性 全域物件 Number
獲得幾個新屬性
Number.EPSILON
用於比較浮點數,並容許捨入誤差。Number.isInteger(num)
檢查 num
是否為整數(沒有小數部分的數字)
> Number.isInteger(1.05)
false
> Number.isInteger(1)
true
> Number.isInteger(-3.1)
false
> Number.isInteger(-3)
true
Number.isSafeInteger(number)
Number.MIN_SAFE_INTEGER
Number.MAX_SAFE_INTEGER
Number.isNaN(num)
檢查 num
是否為值 NaN
。與全域函式 isNaN()
相反,它不會強制轉換其引數為數字,因此對於非數字來說更安全
> isNaN('???')
true
> Number.isNaN('???')
false
Number
的三個額外方法大多等同於具有相同名稱的全域函式:Number.isFinite
、Number.parseFloat
、Number.parseInt
。Math
方法 全域物件 Math
有新的方法,用於數值、三角函數和位元運算。我們來看四個範例。
Math.sign()
傳回數字的符號
> Math.sign(-8)
-1
> Math.sign(0)
0
> Math.sign(3)
1
Math.trunc()
移除了數字的小數部分
> Math.trunc(3.1)
3
> Math.trunc(3.9)
3
> Math.trunc(-3.1)
-3
> Math.trunc(-3.9)
-3
Math.log10()
計算以 10 為底的對數
> Math.log10(100)
2
Math.hypot()
計算其引數平方和的平方根(畢氏定理)
> Math.hypot(3, 4)
5
新的字串方法
> 'hello'.startsWith('hell')
true
> 'hello'.endsWith('ello')
true
> 'hello'.includes('ell')
true
> 'doo '.repeat(3)
'doo doo doo '
ES6 有一種新的字串文字,稱為範本文字
// String interpolation via template literals (in backticks)
const
first
=
'Jane'
;
const
last
=
'Doe'
;
console
.
log
(
`Hello
${
first
}
${
last
}
!`
);
// Hello Jane Doe!
// Template literals also let you create strings with multiple lines
const
multiLine
=
`
This is
a string
with multiple
lines`
;
符號是 ECMAScript 6 中新的基本型別。它們是透過工廠函式建立的
const
mySymbol
=
Symbol
(
'mySymbol'
);
每次呼叫工廠函式時,都會建立一個新的唯一符號。選用的參數是一個描述性字串,用於在列印符號時顯示(它沒有其他用途)
> mySymbol
Symbol(mySymbol)
符號主要用作獨特的屬性鍵,符號絕不會與任何其他屬性鍵(符號或字串)衝突。例如,你可以使用儲存在 Symbol.iterator
中的符號作為方法的鍵,讓物件可迭代(可透過 for-of
迴圈和其他語言機制使用),(有關可迭代的更多資訊,請參閱迭代章節)
const
iterableObject
=
{
[
Symbol
.
iterator
]()
{
// (A)
···
}
}
for
(
const
x
of
iterableObject
)
{
console
.
log
(
x
);
}
// Output:
// hello
// world
在第 A 行中,符號用作方法的鍵。這個獨特的標記讓物件可迭代,並讓我們可以使用 for-of
迴圈。
在 ECMAScript 5 中,你可能使用字串來表示顏色等概念。在 ES6 中,你可以使用符號,並確保它們始終是唯一的
const
COLOR_RED
=
Symbol
(
'Red'
);
const
COLOR_ORANGE
=
Symbol
(
'Orange'
);
const
COLOR_YELLOW
=
Symbol
(
'Yellow'
);
const
COLOR_GREEN
=
Symbol
(
'Green'
);
const
COLOR_BLUE
=
Symbol
(
'Blue'
);
const
COLOR_VIOLET
=
Symbol
(
'Violet'
);
function
getComplement
(
color
)
{
switch
(
color
)
{
case
COLOR_RED
:
return
COLOR_GREEN
;
case
COLOR_ORANGE
:
return
COLOR_BLUE
;
case
COLOR_YELLOW
:
return
COLOR_VIOLET
;
case
COLOR_GREEN
:
return
COLOR_RED
;
case
COLOR_BLUE
:
return
COLOR_ORANGE
;
case
COLOR_VIOLET
:
return
COLOR_YELLOW
;
default
:
throw
new
Exception
(
'Unknown color: '
+
color
);
}
}
每次呼叫 Symbol('Red')
時,都會建立一個新的符號。因此,COLOR_RED
絕不會被誤認為另一個值。如果它是字串 'Red'
,情況就會不同。
將符號強制轉換(隱式轉換)為字串會擲回例外
const
sym
=
Symbol
(
'desc'
);
const
str1
=
''
+
sym
;
// TypeError
const
str2
=
`
${
sym
}
`
;
// TypeError
唯一的解決方案是明確轉換
const
str2
=
String
(
sym
);
// 'Symbol(desc)'
const
str3
=
sym
.
toString
();
// 'Symbol(desc)'
禁止強制轉換可以防止一些錯誤,但也會讓使用符號變得更複雜。
下列操作知道符號作為屬性鍵
Reflect.ownKeys()
[]
進行屬性存取
Object.assign()
下列操作忽略符號作為屬性鍵
Object.keys()
Object.getOwnPropertyNames()
for-in
迴圈ES6 有兩種新的字面值:範本字串和標籤範本字串。這兩種字面值名稱相似,看起來也很相似,但它們有很大的不同。因此,區分它們很重要
範本字串是可跨多行且包含內插運算式(透過 ${···}
插入)的字串字面值
const
firstName
=
'Jane'
;
console
.
log
(
`Hello
${
firstName
}
!
How are you
today?`
);
// Output:
// Hello Jane!
// How are you
// today?
標記範本字串(簡稱:標記範本)是透過在範本字串之前提及函式來建立
> String.raw`A \tagged\ template`
'A \\tagged\\ template'
標記範本是函式呼叫。在先前的範例中,呼叫 String.raw
方法來產生標記範本的結果。
ES6 提供兩種宣告變數的新方法:let
和 const
,它們大部分取代了 ES5 宣告變數的方法 var
。
let
let
的運作方式類似於 var
,但它宣告的變數為區塊範圍,它只存在於目前的區塊中。var
為函式範圍。
在以下程式碼中,您可以看到 let
宣告的變數 tmp
只存在於從 A 行開始的區塊中
function
order
(
x
,
y
)
{
if
(
x
>
y
)
{
// (A)
let
tmp
=
x
;
x
=
y
;
y
=
tmp
;
}
console
.
log
(
tmp
===
x
);
// ReferenceError: tmp is not defined
return
[
x
,
y
];
}
const
const
的運作方式類似於 let
,但您宣告的變數必須立即初始化,且其值無法在之後變更。
const
foo
;
// SyntaxError: missing = in const declaration
const
bar
=
123
;
bar
=
456
;
// TypeError: `bar` is read-only
由於 for-of
會為每個迴圈反覆運算建立一個繫結(變數的儲存空間),因此可以 const
宣告迴圈變數
for
(
const
x
of
[
'a'
,
'b'
])
{
console
.
log
(
x
);
}
// Output:
// a
// b
下表概述了 ES6 中宣告變數的六種方法(靈感來自 kangax 的表格)
提升 | 範圍 | 建立全域屬性 | |
---|---|---|---|
var |
宣告 | 函式 | 是 |
let |
暫時性死區 | 區塊 | 否 |
const |
暫時性死區 | 區塊 | 否 |
函式 |
完整 | 區塊 | 是 |
類別 |
否 | 區塊 | 否 |
import |
完整 | 模組全域 | 否 |
解構是一種從儲存在(可能是巢狀)物件和陣列中的資料中提取多個值的便利方法。它可用於接收資料的位置(例如指派的左側)。如何提取值是透過模式指定的(請繼續閱讀以取得範例)。
解構物件
const
obj
=
{
first
:
'Jane'
,
last
:
'Doe'
};
const
{
first
:
f
,
last
:
l
}
=
obj
;
// f = 'Jane'; l = 'Doe'
// {prop} is short for {prop: prop}
const
{
first
,
last
}
=
obj
;
// first = 'Jane'; last = 'Doe'
解構有助於處理回傳值
const
obj
=
{
foo
:
123
};
const
{
writable
,
configurable
}
=
Object
.
getOwnPropertyDescriptor
(
obj
,
'foo'
);
console
.
log
(
writable
,
configurable
);
// true true
陣列解構(適用於所有可迭代值)
const
iterable
=
[
'a'
,
'b'
];
const
[
x
,
y
]
=
iterable
;
// x = 'a'; y = 'b'
解構有助於處理回傳值
const
[
all
,
year
,
month
,
day
]
=
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.
exec
(
'2999-12-31'
);
解構可以在以下位置使用(我示範陣列模式;物件模式也一樣)
// Variable declarations:
const
[
x
]
=
[
'a'
];
let
[
x
]
=
[
'a'
];
var
[
x
]
=
[
'a'
];
// Assignments:
[
x
]
=
[
'a'
];
// Parameter definitions:
function
f
([
x
])
{
···
}
f
([
'a'
]);
您也可以在 for-of
迴圈中解構
const
arr
=
[
'a'
,
'b'
];
for
(
const
[
index
,
element
]
of
arr
.
entries
())
{
console
.
log
(
index
,
element
);
}
// Output:
// 0 a
// 1 b
ECMAScript 6 中的參數處理已大幅升級。現在支援參數預設值、剩餘參數(變數參數)和解構。
此外,展開運算子有助於函式/方法/建構式呼叫和陣列文字。
預設參數值是透過等號 (=
) 為參數指定的。如果呼叫者未提供參數值,則會使用預設值。在以下範例中,y
的預設參數值為 0
function
func
(
x
,
y
=
0
)
{
return
[
x
,
y
];
}
func
(
1
,
2
);
// [1, 2]
func
(
1
);
// [1, 0]
func
();
// [undefined, 0]
如果您在參數名稱前加上剩餘運算子 (...
),該參數會透過陣列接收所有剩餘參數
function
format
(
pattern
,
...
params
)
{
return
{
pattern
,
params
};
}
format
(
1
,
2
,
3
);
// { pattern: 1, params: [ 2, 3 ] }
format
();
// { pattern: undefined, params: [] }
如果您在參數清單中使用物件模式進行解構,您可以模擬命名參數
function
selectEntries
({
start
=
0
,
end
=-
1
,
step
=
1
}
=
{})
{
// (A)
// The object pattern is an abbreviation of:
// { start: start=0, end: end=-1, step: step=1 }
// Use the variables `start`, `end` and `step` here
···
}
selectEntries
({
start
:
10
,
end
:
30
,
step
:
2
});
selectEntries
({
step
:
3
});
selectEntries
({});
selectEntries
();
A 行中的 = {}
讓您可以不用參數呼叫 selectEntries()
。
...
) 在函式和建構式呼叫中,展開運算子會將可迭代值轉換為引數
>
Math
.
max
(
-
1
,
5
,
11
,
3
)
11
>
Math
.
max
(...[
-
1
,
5
,
11
,
3
])
11
>
Math
.
max
(
-
1
,
...[
-
5
,
11
],
3
)
11
在陣列文字中,展開運算子會將可迭代值轉換為陣列元素
>
[
1
,
...[
2
,
3
],
4
]
[
1
,
2
,
3
,
4
]
在 ES5 中,單一建構(傳統)函式扮演三個角色
在 ES6 中,有更多專業化。現在由下列方式處理這三個職責。就函式定義和類別定義而言,定義不是宣告就是表達式。
特別是對於回呼函式,箭頭函式很方便,因為它們不會遮蔽周圍範圍的 this
。
對於較長的回呼函式和獨立函式,傳統函式可以。有些 API 使用 this
作為隱含參數。在這種情況下,你別無選擇,只能使用傳統函式。
請注意,我區分
即使它們的行為不同(如下所述),所有這些實體都是函式。例如
> typeof (() => {}) // arrow function
'function'
> typeof function* () {} // generator function
'function'
> typeof class {} // class
'function'
箭頭函式有兩個好處。
首先,它們比傳統函式表達式簡潔
const
arr
=
[
1
,
2
,
3
];
const
squares
=
arr
.
map
(
x
=>
x
*
x
);
// Traditional function expression:
const
squares
=
arr
.
map
(
function
(
x
)
{
return
x
*
x
});
其次,它們的 this
從周圍環境(詞彙)中擷取。因此,你不再需要 bind()
或 that = this
。
function
UiComponent
()
{
const
button
=
document
.
getElementById
(
'myButton'
);
button
.
addEventListener
(
'click'
,
()
=>
{
console
.
log
(
'CLICK'
);
this
.
handleClick
();
// lexical `this`
});
}
以下變數在箭頭函式內部都是詞彙的
arguments
super
this
new.target
方法定義
const
obj
=
{
myMethod
(
x
,
y
)
{
···
}
};
屬性值簡寫
const
first
=
'Jane'
;
const
last
=
'Doe'
;
const
obj
=
{
first
,
last
};
// Same as:
const
obj
=
{
first
:
first
,
last
:
last
};
運算屬性金鑰
const
propKey
=
'foo'
;
const
obj
=
{
[
propKey
]
:
true
,
[
'b'
+
'ar'
]
:
123
};
這個新語法也可以用於方法定義
const
obj
=
{
[
'h'
+
'ello'
]()
{
return
'hi'
;
}
};
console
.
log
(
obj
.
hello
());
// hi
運算屬性金鑰的主要用例是讓它容易使用符號作為屬性金鑰。
Object
中的新方法 Object
最重要的新方法是 assign()
。傳統上,這個功能在 JavaScript 世界中稱為 extend()
。與這個經典操作的運作方式相反,Object.assign()
僅考慮自己的(非繼承的)屬性。
const
obj
=
{
foo
:
123
};
Object
.
assign
(
obj
,
{
bar
:
true
});
console
.
log
(
JSON
.
stringify
(
obj
));
// {"foo":123,"bar":true}
類別和子類別
class
Point
{
constructor
(
x
,
y
)
{
this
.
x
=
x
;
this
.
y
=
y
;
}
toString
()
{
return
`(
${
this
.
x
}
,
${
this
.
y
}
)`
;
}
}
class
ColorPoint
extends
Point
{
constructor
(
x
,
y
,
color
)
{
super
(
x
,
y
);
this
.
color
=
color
;
}
toString
()
{
return
super
.
toString
()
+
' in '
+
this
.
color
;
}
}
使用類別
> const cp = new ColorPoint(25, 8, 'green');
> cp.toString();
'(25, 8) in green'
> cp instanceof ColorPoint
true
> cp instanceof Point
true
在底層,ES6 類別並不是什麼新東西:它們主要提供更方便的語法來建立舊式的建構函式。如果你使用 typeof
,你可以看到
> typeof Point
'function'
JavaScript 長久以來都有模組,但它們是透過函式庫實作,而非內建在語言中。ES6 是 JavaScript 首次內建模組。
ES6 模組儲存在檔案中。每個檔案只有一個模組,每個模組只有一個檔案。你可以用兩種方式從模組中匯出內容。這兩種方式可以混合使用,但通常建議分開使用。
可以有多個命名匯出
//------ lib.js ------
export
const
sqrt
=
Math
.
sqrt
;
export
function
square
(
x
)
{
return
x
*
x
;
}
export
function
diag
(
x
,
y
)
{
return
sqrt
(
square
(
x
)
+
square
(
y
));
}
//------ main.js ------
import
{
square
,
diag
}
from
'lib'
;
console
.
log
(
square
(
11
));
// 121
console
.
log
(
diag
(
4
,
3
));
// 5
你也可以匯入整個模組
//------ main.js ------
import
*
as
lib
from
'lib'
;
console
.
log
(
lib
.
square
(
11
));
// 121
console
.
log
(
lib
.
diag
(
4
,
3
));
// 5
可以有一個預設匯出。例如,一個函式
//------ myFunc.js ------
export
default
function
()
{
···
}
// no semicolon!
//------ main1.js ------
import
myFunc
from
'myFunc'
;
myFunc
();
或一個類別
//------ MyClass.js ------
export
default
class
{
···
}
// no semicolon!
//------ main2.js ------
import
MyClass
from
'MyClass'
;
const
inst
=
new
MyClass
();
請注意,如果你預設匯出函式或類別(它們是匿名宣告),結尾處不會有分號。
指令碼 | 模組 | |
---|---|---|
HTML 元素 | <script> |
<script type="module"> |
預設模式 | 非嚴格 | 嚴格 |
頂層變數是 | 全域 | 模組的區域 |
頂層的 this 值 |
window |
undefined |
執行 | 同步 | 非同步 |
宣告式匯入(import 陳述式) |
否 | 是 |
程式化匯入(基於 Promise 的 API) | 是 | 是 |
檔案副檔名 | .js |
.js |
for-of
迴圈 for-of
是 ES6 中取代 for-in
和 forEach()
的新迴圈,並支援新的迭代協定。
使用它來迴圈可迭代物件(陣列、字串、Map、Set 等;請參閱章節「可迭代物件和迭代器」)
const
iterable
=
[
'a'
,
'b'
];
for
(
const
x
of
iterable
)
{
console
.
log
(
x
);
}
// Output:
// a
// b
break
和 continue
可在 for-of
迴圈中使用
for
(
const
x
of
[
'a'
,
''
,
'b'
])
{
if
(
x
.
length
===
0
)
break
;
console
.
log
(
x
);
}
// Output:
// a
在迴圈陣列時同時存取元素及其索引(of
前面的方括號表示我們正在使用 解構)
const
arr
=
[
'a'
,
'b'
];
for
(
const
[
index
,
element
]
of
arr
.
entries
())
{
console
.
log
(
`
${
index
}
.
${
element
}
`
);
}
// Output:
// 0. a
// 1. b
迴圈 Map 中的 [key, value] 項目(of
前面的方括號表示我們正在使用 解構)
const
map
=
new
Map
([
[
false
,
'no'
],
[
true
,
'yes'
],
]);
for
(
const
[
key
,
value
]
of
map
)
{
console
.
log
(
`
${
key
}
=>
${
value
}
`
);
}
// Output:
// false => no
// true => yes
新的靜態 Array
方法
Array.from(arrayLike, mapFunc?, thisArg?)
Array.of(...items)
新的 Array.prototype
方法
Array.prototype.entries()
Array.prototype.keys()
Array.prototype.values()
Array.prototype.find(predicate, thisArg?)
Array.prototype.findIndex(predicate, thisArg?)
Array.prototype.copyWithin(target, start, end=this.length)
Array.prototype.fill(value, start=0, end=this.length)
在 ECMAScript 6 中,以下四種資料結構是新的:Map
、WeakMap
、Set
和 WeakSet
。
Map 的鍵值可以是任意值
> const map = new Map(); // create an empty Map
> const KEY = {};
> map.set(KEY, 123);
> map.get(KEY)
123
> map.has(KEY)
true
> map.delete(KEY);
true
> map.has(KEY)
false
您可以使用包含 [鍵值, 值] 成對的陣列 (或任何可迭代物件) 來設定 Map 中的初始資料
const
map
=
new
Map
([
[
1
,
'one'
],
[
2
,
'two'
],
[
3
,
'three'
],
// trailing comma is ignored
]);
Set 是唯一元素的集合
const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]
如您所見,如果您將可迭代物件 (範例中的 arr
) 傳遞給建構函式,則可以使用元素初始化 Set。
WeakMap 是一種 Map,不會阻止其鍵值被垃圾回收。這表示您可以將資料與物件關聯,而不用擔心記憶體外洩。例如
//----- Manage listeners
const
_objToListeners
=
new
WeakMap
();
function
addListener
(
obj
,
listener
)
{
if
(
!
_objToListeners
.
has
(
obj
))
{
_objToListeners
.
set
(
obj
,
new
Set
());
}
_objToListeners
.
get
(
obj
).
add
(
listener
);
}
function
triggerListeners
(
obj
)
{
const
listeners
=
_objToListeners
.
get
(
obj
);
if
(
listeners
)
{
for
(
const
listener
of
listeners
)
{
listener
();
}
}
}
//----- Example: attach listeners to an object
const
obj
=
{};
addListener
(
obj
,
()
=>
console
.
log
(
'hello'
));
addListener
(
obj
,
()
=>
console
.
log
(
'world'
));
//----- Example: trigger listeners
triggerListeners
(
obj
);
// Output:
// hello
// world
類型化陣列是 ECMAScript 6 API,用於處理二進位資料。
程式碼範例
const
typedArray
=
new
Uint8Array
([
0
,
1
,
2
]);
console
.
log
(
typedArray
.
length
);
// 3
typedArray
[
0
]
=
5
;
const
normalArray
=
[...
typedArray
];
// [5,1,2]
// The elements are stored in typedArray.buffer.
// Get a different view on the same data:
const
dataView
=
new
DataView
(
typedArray
.
buffer
);
console
.
log
(
dataView
.
getUint8
(
0
));
// 5
ArrayBuffer
的執行個體儲存要處理的二進位資料。使用兩種「檢視」來存取資料
Uint8Array
、Int16Array
、Float32Array
等) 將 ArrayBuffer 解釋為單一類型元素的索引順序。DataView
的執行個體讓您可以在 ArrayBuffer 內的任何位元組偏移量中,將資料存取為多種型態的元素 (Uint8
、Int16
、Float32
等)。下列瀏覽器 API 支援類型化陣列 (詳細資訊會在專門的章節中說明)
ES6 引入了新的機制來遍歷資料:迭代。迭代有兩個核心概念
Symbol.iterator
的方法來做到這一點。該方法是迭代器的工廠。以 TypeScript 表示法表示為介面,這些角色如下所示
interface
Iterable
{
[
Symbol
.
iterator
]()
:
Iterator
;
}
interface
Iterator
{
next
()
:
IteratorResult
;
}
interface
IteratorResult
{
value
:
any
;
done
:
boolean
;
}
下列值是可迭代的
一般物件不可迭代(原因說明於 專門章節)。
透過迭代存取資料的語言建構
const
[
a
,
b
]
=
new
Set
([
'a'
,
'b'
,
'c'
]);
for-of
迴圈
for
(
const
x
of
[
'a'
,
'b'
,
'c'
])
{
console
.
log
(
x
);
}
Array.from()
:
const
arr
=
Array
.
from
(
new
Set
([
'a'
,
'b'
,
'c'
]));
...
)
const
arr
=
[...
new
Set
([
'a'
,
'b'
,
'c'
])];
const
map
=
new
Map
([[
false
,
'no'
],
[
true
,
'yes'
]]);
const
set
=
new
Set
([
'a'
,
'b'
,
'c'
]);
Promise.all()
、Promise.race()
Promise
.
all
(
iterableOverPromises
).
then
(
···
);
Promise
.
race
(
iterableOverPromises
).
then
(
···
);
yield*
:
yield
*
anIterable
;
您可以將產生器視為可以暫停和繼續的程序(程式碼片段)
function
*
genFunc
()
{
// (A)
console
.
log
(
'First'
);
yield
;
console
.
log
(
'Second'
);
}
請注意新的語法:function*
是產生器函式的新「關鍵字」(還有產生器方法)。yield
是產生器可以用來暫停自己的運算子。此外,產生器還可以透過 yield
接收輸入和傳送輸出。
當您呼叫產生器函式 genFunc()
時,您會取得一個產生器物件 genObj
,您可以用它來控制程序
const
genObj
=
genFunc
();
此處理程序最初在 A 行暫停。genObj.next()
繼續執行,genFunc()
內部的 yield
暫停執行
genObj
.
next
();
// Output: First
genObj
.
next
();
// output: Second
有四種產生器
function
*
genFunc
()
{
···
}
const
genObj
=
genFunc
();
const
genFunc
=
function
*
()
{
···
};
const
genObj
=
genFunc
();
const
obj
=
{
*
generatorMethod
()
{
···
}
};
const
genObj
=
obj
.
generatorMethod
();
class
MyClass
{
*
generatorMethod
()
{
···
}
}
const
myInst
=
new
MyClass
();
const
genObj
=
myInst
.
generatorMethod
();
產生器傳回的物件可迭代;每個 yield
都會貢獻給迭代值序列。因此,您可以使用產生器來實作可迭代物件,而各種 ES6 語言機制可以使用這些物件:for-of
迴圈、展開運算子 (...
) 等。
下列函式傳回物件屬性的可迭代物件,每個屬性會傳回一個 [key, value] 成對值
function
*
objectEntries
(
obj
)
{
const
propKeys
=
Reflect
.
ownKeys
(
obj
);
for
(
const
propKey
of
propKeys
)
{
// `yield` returns a value and then pauses
// the generator. Later, execution continues
// where it was previously paused.
yield
[
propKey
,
obj
[
propKey
]];
}
}
objectEntries()
的使用方式如下
const
jane
=
{
first
:
'Jane'
,
last
:
'Doe'
};
for
(
const
[
key
,
value
]
of
objectEntries
(
jane
))
{
console
.
log
(
`
${
key
}
:
${
value
}
`
);
}
// Output:
// first: Jane
// last: Doe
objectEntries()
的運作方式詳細說明於 專屬區段。在沒有產生器的情況下實作相同的功能會困難許多。
您可以使用產生器大幅簡化 Promise 的使用。我們來看一個基於 Promise 的函式 fetchJson()
,以及如何透過產生器來改善它。
function
fetchJson
(
url
)
{
return
fetch
(
url
)
.
then
(
request
=>
request
.
text
())
.
then
(
text
=>
{
return
JSON
.
parse
(
text
);
})
.
catch
(
error
=>
{
console
.
log
(
`ERROR:
${
error
.
stack
}
`
);
});
}
使用 函式庫 co 和產生器,這個非同步程式碼看起來像同步程式碼
const
fetchJson
=
co
.
wrap
(
function
*
(
url
)
{
try
{
let
request
=
yield
fetch
(
url
);
let
text
=
yield
request
.
text
();
return
JSON
.
parse
(
text
);
}
catch
(
error
)
{
console
.
log
(
`ERROR:
${
error
.
stack
}
`
);
}
});
ECMAScript 2017 將有非同步函式,其內部基於產生器。使用它們,程式碼看起來像這樣
async
function
fetchJson
(
url
)
{
try
{
let
request
=
await
fetch
(
url
);
let
text
=
await
request
.
text
();
return
JSON
.
parse
(
text
);
}
catch
(
error
)
{
console
.
log
(
`ERROR:
${
error
.
stack
}
`
);
}
}
所有版本都可以像這樣呼叫
fetchJson
(
'http://example.com/some_file.json'
)
.
then
(
obj
=>
console
.
log
(
obj
));
產生器可以透過 yield
從 next()
接收輸入。這表示當新資料非同步抵達時,您可以喚醒產生器,而產生器會覺得它同步接收資料。
下列正規表示式功能是 ECMAScript 6 的新功能
/y
(sticky) 將正規表示式的每個比對結果錨定在前一個比對結果的結尾。/u
(unicode) 將代理對 (例如 \uD83D\uDE80
) 視為碼點處理,並讓您在正規表示式中使用 Unicode 碼點跳脫 (例如 \u{1F680}
)。flags
讓您可以存取正規表示式的旗標,就像 source
已經讓您在 ES5 中存取模式一樣
> /abc/ig.source // ES5
'abc'
> /abc/ig.flags // ES6
'gi'
RegExp()
來製作正規表示式的副本
> new RegExp(/abc/ig).flags
'gi'
> new RegExp(/abc/ig, 'i').flags // change flags
'i'
Promise 是提供非同步運算結果的替代方案,相較於回呼函式。它需要非同步函式實作者的更多努力,但為這些函式的使用者提供多項好處。
下列函式透過 Promise 非同步傳回結果
function
asyncFunc
()
{
return
new
Promise
(
function
(
resolve
,
reject
)
{
···
resolve
(
result
);
···
reject
(
error
);
});
}
您呼叫 asyncFunc()
的方式如下
asyncFunc
()
.
then
(
result
=>
{
···
})
.
catch
(
error
=>
{
···
});
then()
呼叫 then()
永遠傳回一個 Promise,讓您可以串連方法呼叫
asyncFunc1
()
.
then
(
result1
=>
{
// Use result1
return
asyncFunction2
();
// (A)
})
.
then
(
result2
=>
{
// (B)
// Use result2
})
.
catch
(
error
=>
{
// Handle errors of asyncFunc1() and asyncFunc2()
});
then()
傳回的 Promise P 如何解決,取決於其回呼函式執行什麼動作
asyncFunction2
的 Promise 的解決。此外,請注意 catch()
如何處理兩個非同步函式呼叫 (asyncFunction1()
和 asyncFunction2()
) 的錯誤。也就是說,未捕捉的錯誤會傳遞下去,直到出現錯誤處理常式。
如果您透過 then()
串連非同步函式呼叫,則它們會依序執行,一次一個
asyncFunc1
()
.
then
(()
=>
asyncFunc2
());
如果您不這麼做,而是立即呼叫所有函式,則它們基本上會平行執行 (在 Unix 程序術語中稱為「fork」)
asyncFunc1
();
asyncFunc2
();
Promise.all()
讓您可以在所有結果都出來時收到通知 (在 Unix 程序術語中稱為「join」)。它的輸入是一個 Promise 陣列,其輸出是一個單一 Promise,並以結果陣列來完成。
Promise
.
all
([
asyncFunc1
(),
asyncFunc2
(),
])
.
then
(([
result1
,
result2
])
=>
{
···
})
.
catch
(
err
=>
{
// Receives first rejection among the Promises
···
});
Promise API 旨在非同步傳遞結果。Promise 物件(簡稱:Promise)是結果的替代品,透過該物件傳遞結果。
狀態
對狀態變更做出反應
then()
註冊的回呼,用於接收已完成或已拒絕的通知。then()
方法的物件。每當 API 僅有興趣接收解決通知時,它只要求可 then 物件(例如從 then()
和 catch()
傳回的值;或傳遞給 Promise.all()
和 Promise.race()
的值)。變更狀態:有兩個操作可用於變更 Promise 的狀態。在您執行其中一個操作一次後,後續執行將不產生任何效果。
代理讓您可以攔截並自訂對物件執行的操作(例如取得屬性)。它們是一種元程式設計功能。
在以下範例中,proxy
是我們攔截其運算的物件,而 handler
是處理攔截的物件。在這個案例中,我們只攔截單一運算,get
(取得屬性)。
const
target
=
{};
const
handler
=
{
get
(
target
,
propKey
,
receiver
)
{
console
.
log
(
'get '
+
propKey
);
return
123
;
}
};
const
proxy
=
new
Proxy
(
target
,
handler
);
當我們取得屬性 proxy.foo
時,處理常式攔截該運算
> proxy.foo
get foo
123
參閱 完整 API 參考,以取得可攔截運算的清單。