30. ES6 新特性概述
目录
请支持本书:购买 (PDF, EPUB, MOBI)捐赠
(广告,请勿屏蔽。)

30. ES6 新特性概述

本章收集了本书所有章节的概述部分。



30.1 ES6 特性分类

ES6 规范的介绍列出了所有新特性

ECMAScript 6 的一些主要增强功能包括模块、类声明、词法块作用域、迭代器和生成器、用于异步编程的 Promise、解构模式以及适当的尾调用。ECMAScript 内置库已经扩展,以支持更多的数据抽象,包括映射、集合和二进制数值数组,以及对字符串和正则表达式中 Unicode 补充字符的额外支持。现在可以通过子类化扩展内置库。

主要有三类特性

30.2 新的数字和 Math 特性

30.2.1 新的整数字面量

现在可以使用二进制和八进制表示法指定整数

> 0xFF // ES5: hexadecimal
255
> 0b11 // ES6: binary
3
> 0o10 // ES6: octal
8

30.2.2 新的 Number 属性

全局对象 Number 获得了一些新属性

30.2.3 新的 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    

30.3 新的字符串特性

新的字符串方法

> '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`;

30.4 符号

符号是 ECMAScript 6 中的一种新的原始类型。它们通过工厂函数创建

const mySymbol = Symbol('mySymbol');

每次调用工厂函数时,都会创建一个新的唯一符号。可选参数是一个描述性字符串,在打印符号时显示(它没有其他用途)

> mySymbol
Symbol(mySymbol)

30.4.1 用例 1:唯一的属性键

符号主要用作唯一的属性键——符号永远不会与任何其他属性键(符号或字符串)冲突。例如,可以通过使用存储在 Symbol.iterator 中的符号作为方法的键,使对象“可迭代”(可通过 for-of 循环和其他语言机制使用)(有关可迭代对象的更多信息,请参见关于迭代的章节

const iterableObject = {
    [Symbol.iterator]() { // (A)
        ···
    }
}
for (const x of iterableObject) {
    console.log(x);
}
// Output:
// hello
// world

在 A 行中,一个符号被用作方法的键。这个唯一的标记使对象可迭代,并使我们能够使用 for-of 循环。

30.4.2 用例 2:表示概念的常量

在 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',情况就会有所不同。

30.4.3 陷阱:不能将符号强制转换为字符串

将符号强制(隐式转换)为字符串会引发异常

const sym = Symbol('desc');

const str1 = '' + sym; // TypeError
const str2 = `${sym}`; // TypeError

唯一的解决办法是显式转换

const str2 = String(sym); // 'Symbol(desc)'
const str3 = sym.toString(); // 'Symbol(desc)'

禁止强制转换可以防止一些错误,但也会使使用符号变得更加复杂。

以下操作识别符号作为属性键

以下操作忽略符号作为属性键

30.5 模板字面量

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 方法来生成标签模板的结果。

30.6 变量和作用域

ES6 提供了两种声明变量的新方法:letconst,它们在很大程度上取代了 ES5 中声明变量的方法 var

30.6.1 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];
}

30.6.2 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

30.6.3 声明变量的方式

下表概述了在 ES6 中声明变量的六种方式(灵感来自 kangax 的表格

  提升 作用域 创建全局属性
var 声明 函数
let 暂时性死区
const 暂时性死区
函数 完整
class
import 完整 模块全局

30.7 解构

“解构”是一种从存储在(可能是嵌套的)对象和数组中的数据中提取多个值的便捷方法。它可以在接收数据的位置使用(例如赋值语句的左侧)。如何提取值是通过模式指定的(请继续阅读以获取示例)。

30.7.1 对象解构

解构对象

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

30.7.2 数组解构

数组解构(适用于所有可迭代值)

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');

30.7.3 解构可以在哪里使用?

解构可以在以下位置使用(我展示了数组模式来演示;对象模式也可以使用)

// 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

30.8 参数处理

ECMAScript 6 中的参数处理已得到显著升级。它现在支持参数默认值、剩余参数 (varargs) 和解构。

此外,扩展运算符有助于函数/方法/构造函数调用和数组字面量。

30.8.1 默认参数值

通过等号 (=) 为参数指定*默认参数值*。如果调用者未提供参数值,则使用默认值。在以下示例中,y 的默认参数值为 0

function func(x, y=0) {
    return [x, y];
}
func(1, 2); // [1, 2]
func(1); // [1, 0]
func(); // [undefined, 0]

30.8.2 剩余参数

如果使用剩余运算符 (...) 作为参数名称的前缀,则该参数将通过数组接收所有剩余参数

function format(pattern, ...params) {
    return {pattern, params};
}
format(1, 2, 3);
    // { pattern: 1, params: [ 2, 3 ] }
format();
    // { pattern: undefined, params: [] }

30.8.3 通过解构的命名参数

如果在参数列表中使用对象模式进行解构,则可以模拟命名参数

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()

30.8.4 扩展运算符 (...)

在函数和构造函数调用中,扩展运算符将可迭代值转换为参数

> 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]

30.9 ECMAScript 6 中的可调用实体

在 ES5 中,单个结构(传统函数)扮演着三个角色

在 ES6 中,有更多专业化。现在,这三个职责的处理方式如下。就函数定义和类定义而言,定义是声明或表达式。

特别是对于回调,箭头函数很方便,因为它们不会遮蔽周围作用域的 this

对于较长的回调和独立函数,传统函数是可以的。一些 API 使用 this 作为隐式参数。在这种情况下,您别无选择,只能使用传统函数。

请注意,我区分了

尽管它们的行为有所不同(稍后解释),但所有这些实体都是函数。例如

> typeof (() => {}) // arrow function
'function'
> typeof function* () {} // generator function
'function'
> typeof class {} // class
'function'

30.10 箭头函数

箭头函数有两个优点。

首先,它们比传统的函数表达式更简洁

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`
    });
}

以下变量在箭头函数内部都是词法的

30.11 类之外的新 OOP 特性

30.11.1 新的对象字面量特性

方法定义

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

计算属性键的主要用例是使使用符号作为属性键变得容易。

30.11.2 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}

30.12

一个类和一个子类

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'

30.13 模块

JavaScript 已经存在模块很长时间了。但是,它们是通过库实现的,而不是内置于语言中。ES6 是 JavaScript 首次拥有内置模块。

ES6 模块存储在文件中。每个文件只有一个模块,每个模块只有一个文件。您可以通过两种方式从模块中导出内容。这两种方式可以混合使用,但通常最好单独使用它们。

30.13.1 多个命名导出

可以有多个*命名导出*

//------ 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

30.13.2 单个默认导出

可以有一个*默认导出*。例如,一个函数

//------ 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();

请注意,如果默认导出函数或类(它们是匿名声明),则末尾没有分号。

30.13.3 浏览器:脚本与模块

  脚本 模块
HTML 元素 <script> <script type="module">
默认模式 非严格 严格
顶级变量是 全局的 模块本地的
顶级 this 的值 window undefined
执行 同步 异步
声明式导入(import 语句)
程序化导入(基于 Promise 的 API)
文件扩展名 .js .js

30.14 for-of 循环

for-of 是 ES6 中的一个新循环,它取代了 for-inforEach(),并支持新的迭代协议。

使用它来循环遍历*可迭代*对象(数组、字符串、映射、集合等;请参阅“可迭代对象和迭代器”一章)

const iterable = ['a', 'b'];
for (const x of iterable) {
    console.log(x);
}

// Output:
// a
// b

breakcontinuefor-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

30.15 新的数组特性

新的静态 Array 方法

新的 Array.prototype 方法

30.16 映射和集合

除其他外,ECMAScript 6 中新增了以下四种数据结构:MapWeakMapSetWeakSet

30.16.1 映射

映射的键可以是任意值

> 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
]);

30.16.2 集合

集合是唯一元素的集合

const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]

如您所见,如果将包含这些元素的可迭代对象(示例中的 arr)传递给构造函数,则可以使用元素初始化集合。

30.16.3 弱映射

弱映射是一种映射,它不会阻止对其键进行垃圾回收。这意味着您可以将数据与对象关联起来,而不必担心内存泄漏。例如

//----- 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

30.17 类型化数组

类型化数组是 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 的实例存储要处理的二进制数据。使用两种*视图*来访问数据

以下浏览器 API 支持类型化数组(详细信息在专门章节中提及

30.18 可迭代对象和迭代器

ES6 引入了一种遍历数据的新机制:*迭代*。迭代有两个核心概念

用 TypeScript 表示法表示为接口,这些角色如下所示

interface Iterable {
    [Symbol.iterator]() : Iterator;
}
interface Iterator {
    next() : IteratorResult;
}
interface IteratorResult {
    value: any;
    done: boolean;
}

30.18.1 可迭代值

以下值是可迭代的

普通对象不可迭代(原因在专门章节中解释)。

30.18.2 支持迭代的结构

通过迭代访问数据的语言结构

30.19 生成器

30.19.1 什么是生成器?

您可以将生成器视为可以暂停和恢复的进程(代码片段)

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

30.19.2 生成器的种类

生成器有四种

  1. 生成器函数声明
     function* genFunc() { ··· }
     const genObj = genFunc();
    
  2. 生成器函数表达式
     const genFunc = function* () { ··· };
     const genObj = genFunc();
    
  3. 对象字面量中的生成器方法定义
     const obj = {
         * generatorMethod() {
             ···
         }
     };
     const genObj = obj.generatorMethod();
    
  4. 类定义(类声明或类表达式)中的生成器方法定义
     class MyClass {
         * generatorMethod() {
             ···
         }
     }
     const myInst = new MyClass();
     const genObj = myInst.generatorMethod();
    

30.19.3 用例:实现可迭代对象

生成器返回的对象是可迭代的;每个 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() 的确切工作原理在专门的章节中进行了解释。如果没有生成器,实现相同的功能需要做更多的工作。

30.19.4 用例:更简单的异步代码

您可以使用生成器来极大地简化 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));

30.19.5 用例:接收异步数据

生成器可以通过 yieldnext() 接收输入。这意味着您可以在异步到达新数据时唤醒生成器,并且对于生成器来说,它感觉就像同步接收数据一样。

30.20 新的正则表达式特性

以下正则表达式特性是 ECMAScript 6 中新增的

30.21 用于异步编程的 Promise

Promise 是回调的替代方案,用于传递异步计算的结果。它们需要异步函数的实现者付出更多努力,但为这些函数的用户提供了几个好处。

以下函数通过 Promise 异步返回结果

function asyncFunc() {
    return new Promise(
        function (resolve, reject) {
            ···
            resolve(result);
            ···
            reject(error);
        });
}

您可以按如下方式调用 asyncFunc()

asyncFunc()
.then(result => { ··· })
.catch(error => { ··· });

30.21.1 链接 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 如何解决取决于其回调的操作

此外,请注意 catch() 如何处理两个异步函数调用(asyncFunction1()asyncFunction2())的错误。也就是说,未捕获的错误会一直传递,直到遇到错误处理程序。

30.21.2 并行执行异步函数

如果您通过 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
    ···
});

30.21.3 词汇表:Promise

Promise API 用于异步传递结果。*Promise 对象*(简称:Promise)是结果的占位符,结果通过该对象传递。

状态

对状态更改做出反应

更改状态:有两个操作可以更改 Promise 的状态。在您调用其中任何一个操作一次后,进一步的调用将无效。

30.22 使用代理进行元编程

代理使您能够拦截和自定义对对象执行的操作(例如获取属性)。它们是一种*元编程*特性。

在以下示例中,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 的参考

下一页:注释