本章收集了本书所有章节的概述部分。
Math 特性Number 属性Math 方法letconst...)Object 中的新方法for-of 循环then()ES6 规范的介绍列出了所有新特性
ECMAScript 6 的一些主要增强功能包括模块、类声明、词法块作用域、迭代器和生成器、用于异步编程的 Promise、解构模式以及适当的尾调用。ECMAScript 内置库已经扩展,以支持更多的数据抽象,包括映射、集合和二进制数值数组,以及对字符串和正则表达式中 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_INTEGERNumber.MAX_SAFE_INTEGERNumber.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 |
暂时性死区 | 块 | 否 |
函数 |
完整 | 块 | 是 |
class |
否 | 块 | 否 |
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 中的参数处理已得到显著升级。它现在支持参数默认值、剩余参数 (varargs) 和解构。
此外,扩展运算符有助于函数/方法/构造函数调用和数组字面量。
通过等号 (=) 为参数指定*默认参数值*。如果调用者未提供参数值,则使用默认值。在以下示例中,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`
});
}
以下变量在箭头函数内部都是词法的
argumentssuperthisnew.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(),并支持新的迭代协议。
使用它来循环遍历*可迭代*对象(数组、字符串、映射、集合等;请参阅“可迭代对象和迭代器”一章)
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
循环遍历映射中的 [键,值] 条目(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。
映射的键可以是任意值
> 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
您可以使用带有 [键,值] 对的数组(或任何可迭代对象)来设置映射中的初始数据
const map = new Map([
[ 1, 'one' ],
[ 2, 'two' ],
[ 3, 'three' ], // trailing comma is ignored
]);
集合是唯一元素的集合
const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]
如您所见,如果将包含这些元素的可迭代对象(示例中的 arr)传递给构造函数,则可以使用元素初始化集合。
弱映射是一种映射,它不会阻止对其键进行垃圾回收。这意味着您可以将数据与对象关联起来,而不必担心内存泄漏。例如
//----- 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 循环、展开运算符 (...) 等。
以下函数返回一个对象的属性的可迭代对象,每个属性对应一个 [键,值] 对
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(粘性)将正则表达式的每个匹配项锚定到先前匹配项的末尾。/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 将使用结果数组来实现。
Promise.all([
asyncFunc1(),
asyncFunc2(),
])
.then(([result1, result2]) => {
···
})
.catch(err => {
// Receives first rejection among the Promises
···
});
Promise API 用于异步传递结果。*Promise 对象*(简称:Promise)是结果的占位符,结果通过该对象传递。
状态
对状态更改做出反应
then() 注册的回调,用于在实现或拒绝时收到通知。then() 方法的对象。每当 API 只想在解决时收到通知时,它只需要可 thenable 对象(例如,从 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 的参考。