1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-29 05:42:20 +00:00

doc: update to ES2018

This commit is contained in:
ruanyf 2018-01-29 08:47:04 +08:00
parent 5d5f588899
commit 14c45fe2f5
5 changed files with 134 additions and 108 deletions

View File

@ -661,7 +661,7 @@ async function logInOrder(urls) {
这里隐含着一个规定,`next`方法必须是同步的,只要调用就必须立刻返回值。也就是说,一旦执行`next`方法,就必须同步地得到`value``done`这两个属性。如果遍历指针正好指向同步操作当然没有问题但对于异步操作就不太合适了。目前的解决方法是Generator 函数里面的异步操作,返回一个 Thunk 函数或者 Promise 对象,即`value`属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而`done`属性则还是同步产生的。 这里隐含着一个规定,`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`这两个属性都是异步产生。
### 异步遍历的接口 ### 异步遍历的接口

View File

@ -1255,11 +1255,11 @@ a // 1
b // [2, 3] b // [2, 3]
``` ```
ES2017 将这个运算符[引入](https://github.com/sebmarkbage/ecmascript-rest-spread)了对象。 ES2018 将这个运算符[引入](https://github.com/sebmarkbage/ecmascript-rest-spread)了对象。
**1解构赋值** ### 解构赋值
对象的解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。 对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的enumerable、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
```javascript ```javascript
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; 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 }) { function wrapperFunction({ x, y, ...restConfig }) {
// 使用x和y参数进行操作 // 使用 x y 参数进行操作
// 其余参数传给原始函数 // 其余参数传给原始函数
return baseFunction(restConfig); return baseFunction(restConfig);
} }
@ -1339,9 +1339,9 @@ function wrapperFunction({ x, y, ...restConfig }) {
上面代码中,原始函数`baseFunction`接受`a``b`作为参数,函数`wrapperFunction``baseFunction`的基础上进行了扩展,能够接受多余的参数,并且保留原始函数的行为。 上面代码中,原始函数`baseFunction`接受`a``b`作为参数,函数`wrapperFunction``baseFunction`的基础上进行了扩展,能够接受多余的参数,并且保留原始函数的行为。
**2扩展运算符** ### 扩展运算符
扩展运算符(`...`)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。 对象的扩展运算符(`...`)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
```javascript ```javascript
let z = { a: 3, b: 4 }; let z = { a: 3, b: 4 };
@ -1419,6 +1419,8 @@ let newVersion = {
```javascript ```javascript
let aWithDefaults = { x: 1, y: 2, ...a }; let aWithDefaults = { x: 1, y: 2, ...a };
// 等同于 // 等同于
even if property keys dont clash, because objects record insertion order:
let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a); let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
// 等同于 // 等同于
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 ```javascript
{...{}, a: 1} {...{}, a: 1}
// { a: 1 } // { a: 1 }
even if property keys dont clash, because objects record insertion order:
``` ```
如果扩展运算符的参数是`null``undefined`,这两个值会被忽略,不会报错。 如果扩展运算符的参数是`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
```

View File

@ -486,6 +486,85 @@ someAsyncThing().then(function() {
上面代码中,第二个`catch`方法用来捕获,前一个`catch`方法抛出的错误。 上面代码中,第二个`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.all`方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。 `Promise.all`方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
@ -747,64 +826,6 @@ Promise.reject(thenable)
上面代码中,`Promise.reject`方法的参数是一个`thenable`对象,执行以后,后面`catch`方法的参数不是`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`
## 应用 ## 应用
### 加载图片 ### 加载图片

View File

@ -117,3 +117,46 @@ class Product {
上面代码中,`throw`都出现在表达式里面。 上面代码中,`throw`都出现在表达式里面。
语法上,`throw`表达式里面的`throw`不再是一个命令,而是一个运算符。为了避免与`throw`命令混淆,规定`throw`出现在行首,一律解释为`throw`语句,而不是`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
```

View File

@ -933,7 +933,7 @@ Breve over the h goes \u{h}ere // 报错
模板字符串会将`\u00FF``\u{42}`当作 Unicode 字符进行转义,所以`\unicode`解析时报错;而`\x56`会被当作十六进制字符串转义,所以`\xerxes`会报错。也就是说,`\u``\x`在 LaTEX 里面有特殊含义,但是 JavaScript 将它们转义了。 模板字符串会将`\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 ```javascript
function tag(strs) { function tag(strs) {