mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-28 21:32:20 +00:00
doc: update to ES2018
This commit is contained in:
parent
5d5f588899
commit
14c45fe2f5
@ -661,7 +661,7 @@ async function logInOrder(urls) {
|
||||
|
||||
这里隐含着一个规定,`next`方法必须是同步的,只要调用就必须立刻返回值。也就是说,一旦执行`next`方法,就必须同步地得到`value`和`done`这两个属性。如果遍历指针正好指向同步操作,当然没有问题,但对于异步操作,就不太合适了。目前的解决方法是,Generator 函数里面的异步操作,返回一个 Thunk 函数或者 Promise 对象,即`value`属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而`done`属性则还是同步产生的。
|
||||
|
||||
目前,有一个[提案](https://github.com/tc39/proposal-async-iteration),为异步操作提供原生的遍历器接口,即`value`和`done`这两个属性都是异步产生,这称为”异步遍历器“(Async Iterator)。
|
||||
ES2018 [引入](https://github.com/tc39/proposal-async-iteration)了”异步遍历器“(Async Iterator),为异步操作提供原生的遍历器接口,即`value`和`done`这两个属性都是异步产生。
|
||||
|
||||
### 异步遍历的接口
|
||||
|
||||
|
@ -1255,11 +1255,11 @@ a // 1
|
||||
b // [2, 3]
|
||||
```
|
||||
|
||||
ES2017 将这个运算符[引入](https://github.com/sebmarkbage/ecmascript-rest-spread)了对象。
|
||||
ES2018 将这个运算符[引入](https://github.com/sebmarkbage/ecmascript-rest-spread)了对象。
|
||||
|
||||
**(1)解构赋值**
|
||||
### 解构赋值
|
||||
|
||||
对象的解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
|
||||
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
|
||||
|
||||
```javascript
|
||||
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
|
||||
@ -1331,7 +1331,7 @@ function baseFunction({ a, b }) {
|
||||
// ...
|
||||
}
|
||||
function wrapperFunction({ x, y, ...restConfig }) {
|
||||
// 使用x和y参数进行操作
|
||||
// 使用 x 和 y 参数进行操作
|
||||
// 其余参数传给原始函数
|
||||
return baseFunction(restConfig);
|
||||
}
|
||||
@ -1339,9 +1339,9 @@ function wrapperFunction({ x, y, ...restConfig }) {
|
||||
|
||||
上面代码中,原始函数`baseFunction`接受`a`和`b`作为参数,函数`wrapperFunction`在`baseFunction`的基础上进行了扩展,能够接受多余的参数,并且保留原始函数的行为。
|
||||
|
||||
**(2)扩展运算符**
|
||||
### 扩展运算符
|
||||
|
||||
扩展运算符(`...`)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
|
||||
对象的扩展运算符(`...`)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
|
||||
|
||||
```javascript
|
||||
let z = { a: 3, b: 4 };
|
||||
@ -1419,6 +1419,8 @@ let newVersion = {
|
||||
```javascript
|
||||
let aWithDefaults = { x: 1, y: 2, ...a };
|
||||
// 等同于
|
||||
even if property keys don’t clash, because objects record insertion order:
|
||||
|
||||
let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
|
||||
// 等同于
|
||||
let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);
|
||||
@ -1438,6 +1440,8 @@ const obj = {
|
||||
```javascript
|
||||
{...{}, a: 1}
|
||||
// { a: 1 }
|
||||
even if property keys don’t clash, because objects record insertion order:
|
||||
|
||||
```
|
||||
|
||||
如果扩展运算符的参数是`null`或`undefined`,这两个值会被忽略,不会报错。
|
||||
@ -1468,45 +1472,3 @@ let runtimeError = {
|
||||
};
|
||||
```
|
||||
|
||||
## Null 传导运算符
|
||||
|
||||
编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取`message.body.user.firstName`,安全的写法是写成下面这样。
|
||||
|
||||
```javascript
|
||||
const firstName = (message
|
||||
&& message.body
|
||||
&& message.body.user
|
||||
&& message.body.user.firstName) || 'default';
|
||||
```
|
||||
|
||||
这样的层层判断非常麻烦,因此现在有一个[提案](https://github.com/claudepache/es-optional-chaining),引入了“Null 传导运算符”(null propagation operator)`?.`,简化上面的写法。
|
||||
|
||||
```javascript
|
||||
const firstName = message?.body?.user?.firstName || 'default';
|
||||
```
|
||||
|
||||
上面代码有三个`?.`运算符,只要其中一个返回`null`或`undefined`,就不再往下运算,而是返回`undefined`。
|
||||
|
||||
“Null 传导运算符”有四种用法。
|
||||
|
||||
- `obj?.prop` // 读取对象属性
|
||||
- `obj?.[expr]` // 同上
|
||||
- `func?.(...args)` // 函数或对象方法的调用
|
||||
- `new C?.(...args)` // 构造函数的调用
|
||||
|
||||
传导运算符之所以写成`obj?.prop`,而不是`obj?prop`,是为了方便编译器能够区分三元运算符`?:`(比如`obj?prop:123`)。
|
||||
|
||||
下面是更多的例子。
|
||||
|
||||
```javascript
|
||||
// 如果 a 是 null 或 undefined, 返回 undefined
|
||||
// 否则返回 a.b.c().d
|
||||
a?.b.c().d
|
||||
|
||||
// 如果 a 是 null 或 undefined,下面的语句不产生任何效果
|
||||
// 否则执行 a.b = 42
|
||||
a?.b = 42
|
||||
|
||||
// 如果 a 是 null 或 undefined,下面的语句不产生任何效果
|
||||
delete a?.b
|
||||
```
|
||||
|
137
docs/promise.md
137
docs/promise.md
@ -486,6 +486,85 @@ someAsyncThing().then(function() {
|
||||
|
||||
上面代码中,第二个`catch`方法用来捕获,前一个`catch`方法抛出的错误。
|
||||
|
||||
## Promise.prototype.finally()
|
||||
|
||||
`finally`方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
|
||||
|
||||
```javascript
|
||||
promise
|
||||
.then(result => {···})
|
||||
.catch(error => {···})
|
||||
.finally(() => {···});
|
||||
```
|
||||
|
||||
上面代码中,不管`promise`最后的状态,在执行完`then`或`catch`指定的回调函数以后,都会执行`finally`方法指定的回调函数。
|
||||
|
||||
下面是一个例子,服务器使用 Promise 处理请求,然后使用`finally`方法关掉服务器。
|
||||
|
||||
```javascript
|
||||
server.listen(port)
|
||||
.then(function () {
|
||||
// ...
|
||||
})
|
||||
.finally(server.stop);
|
||||
```
|
||||
|
||||
`finally`方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是`fulfilled`还是`rejected`。这表明,`finally`方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
|
||||
|
||||
`finally`本质上是`then`方法的特例。
|
||||
|
||||
```javascript
|
||||
promise
|
||||
.finally(() => {
|
||||
// 语句
|
||||
});
|
||||
|
||||
// 等同于
|
||||
promise
|
||||
.then(
|
||||
result => {
|
||||
// 语句
|
||||
return result;
|
||||
},
|
||||
error => {
|
||||
// 语句
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
上面代码中,如果不使用`finally`方法,同样的语句需要为成功和失败两种情况各写一次。有了`finally`方法,则只需要写一次。
|
||||
|
||||
它的实现也很简单。
|
||||
|
||||
```javascript
|
||||
Promise.prototype.finally = function (callback) {
|
||||
let P = this.constructor;
|
||||
return this.then(
|
||||
value => P.resolve(callback()).then(() => value),
|
||||
reason => P.resolve(callback()).then(() => { throw reason })
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
上面代码中,不管前面的 Promise 是`fulfilled`还是`rejected`,都会执行回调函数`callback`。
|
||||
|
||||
从上面的实现还可以看到,`finally`方法总是会返回原来的值。
|
||||
|
||||
```javascript
|
||||
// resolve 的值是 undefined
|
||||
Promise.resolve(2).then(() => {}, () => {})
|
||||
|
||||
// resolve 的值是 2
|
||||
Promise.resolve(2).finally(() => {})
|
||||
|
||||
// reject 的值是 undefined
|
||||
Promise.reject(3).then(() => {}, () => {})
|
||||
|
||||
// reject 的值是 3
|
||||
Promise.reject(3).finally(() => {})
|
||||
```
|
||||
|
||||
## Promise.all()
|
||||
|
||||
`Promise.all`方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
|
||||
@ -747,64 +826,6 @@ Promise.reject(thenable)
|
||||
|
||||
上面代码中,`Promise.reject`方法的参数是一个`thenable`对象,执行以后,后面`catch`方法的参数不是`reject`抛出的“出错了”这个字符串,而是`thenable`对象。
|
||||
|
||||
## 两个有用的附加方法
|
||||
|
||||
ES6 的 Promise API 提供的方法不是很多,有些有用的方法可以自己部署。下面介绍如何部署两个不在 ES6 之中、但很有用的方法。
|
||||
|
||||
### done()
|
||||
|
||||
Promise 对象的回调链,不管以`then`方法或`catch`方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局)。因此,我们可以提供一个`done`方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。
|
||||
|
||||
```javascript
|
||||
asyncFunc()
|
||||
.then(f1)
|
||||
.catch(r1)
|
||||
.then(f2)
|
||||
.done();
|
||||
```
|
||||
|
||||
它的实现代码相当简单。
|
||||
|
||||
```javascript
|
||||
Promise.prototype.done = function (onFulfilled, onRejected) {
|
||||
this.then(onFulfilled, onRejected)
|
||||
.catch(function (reason) {
|
||||
// 抛出一个全局错误
|
||||
setTimeout(() => { throw reason }, 0);
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
从上面代码可见,`done`方法的使用,可以像`then`方法那样用,提供`fulfilled`和`rejected`状态的回调函数,也可以不提供任何参数。但不管怎样,`done`都会捕捉到任何可能出现的错误,并向全局抛出。
|
||||
|
||||
### finally()
|
||||
|
||||
`finally`方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。它与`done`方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。
|
||||
|
||||
下面是一个例子,服务器使用 Promise 处理请求,然后使用`finally`方法关掉服务器。
|
||||
|
||||
```javascript
|
||||
server.listen(0)
|
||||
.then(function () {
|
||||
// run test
|
||||
})
|
||||
.finally(server.stop);
|
||||
```
|
||||
|
||||
它的实现也很简单。
|
||||
|
||||
```javascript
|
||||
Promise.prototype.finally = function (callback) {
|
||||
let P = this.constructor;
|
||||
return this.then(
|
||||
value => P.resolve(callback()).then(() => value),
|
||||
reason => P.resolve(callback()).then(() => { throw reason })
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
上面代码中,不管前面的 Promise 是`fulfilled`还是`rejected`,都会执行回调函数`callback`。
|
||||
|
||||
## 应用
|
||||
|
||||
### 加载图片
|
||||
|
@ -117,3 +117,46 @@ class Product {
|
||||
上面代码中,`throw`都出现在表达式里面。
|
||||
|
||||
语法上,`throw`表达式里面的`throw`不再是一个命令,而是一个运算符。为了避免与`throw`命令混淆,规定`throw`出现在行首,一律解释为`throw`语句,而不是`throw`表达式。
|
||||
|
||||
## Null 传导运算符
|
||||
|
||||
编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取`message.body.user.firstName`,安全的写法是写成下面这样。
|
||||
|
||||
```javascript
|
||||
const firstName = (message
|
||||
&& message.body
|
||||
&& message.body.user
|
||||
&& message.body.user.firstName) || 'default';
|
||||
```
|
||||
|
||||
这样的层层判断非常麻烦,因此现在有一个[提案](https://github.com/claudepache/es-optional-chaining),引入了“Null 传导运算符”(null propagation operator)`?.`,简化上面的写法。
|
||||
|
||||
```javascript
|
||||
const firstName = message?.body?.user?.firstName || 'default';
|
||||
```
|
||||
|
||||
上面代码有三个`?.`运算符,只要其中一个返回`null`或`undefined`,就不再往下运算,而是返回`undefined`。
|
||||
|
||||
“Null 传导运算符”有四种用法。
|
||||
|
||||
- `obj?.prop` // 读取对象属性
|
||||
- `obj?.[expr]` // 同上
|
||||
- `func?.(...args)` // 函数或对象方法的调用
|
||||
- `new C?.(...args)` // 构造函数的调用
|
||||
|
||||
传导运算符之所以写成`obj?.prop`,而不是`obj?prop`,是为了方便编译器能够区分三元运算符`?:`(比如`obj?prop:123`)。
|
||||
|
||||
下面是更多的例子。
|
||||
|
||||
```javascript
|
||||
// 如果 a 是 null 或 undefined, 返回 undefined
|
||||
// 否则返回 a.b.c().d
|
||||
a?.b.c().d
|
||||
|
||||
// 如果 a 是 null 或 undefined,下面的语句不产生任何效果
|
||||
// 否则执行 a.b = 42
|
||||
a?.b = 42
|
||||
|
||||
// 如果 a 是 null 或 undefined,下面的语句不产生任何效果
|
||||
delete a?.b
|
||||
```
|
||||
|
@ -933,7 +933,7 @@ Breve over the h goes \u{h}ere // 报错
|
||||
|
||||
模板字符串会将`\u00FF`和`\u{42}`当作 Unicode 字符进行转义,所以`\unicode`解析时报错;而`\x56`会被当作十六进制字符串转义,所以`\xerxes`会报错。也就是说,`\u`和`\x`在 LaTEX 里面有特殊含义,但是 JavaScript 将它们转义了。
|
||||
|
||||
为了解决这个问题,现在有一个[提案](https://tc39.github.io/proposal-template-literal-revision/),放松对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回`undefined`,而不是报错,并且从`raw`属性上面可以得到原始字符串。
|
||||
为了解决这个问题,ES2018 [放松](https://tc39.github.io/proposal-template-literal-revision/)了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回`undefined`,而不是报错,并且从`raw`属性上面可以得到原始字符串。
|
||||
|
||||
```javascript
|
||||
function tag(strs) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user