1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-25 11:12:21 +00:00
es6tutorial/docs/proposals.md
2018-02-01 18:47:58 +08:00

193 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 最新提案
本章介绍一些尚未进入标准、但很有希望的最新提案。
## do 表达式
本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。
```javascript
{
let t = f();
t = t * t + 1;
}
```
上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到`t`的值,因为块级作用域不返回值,除非`t`是全局变量。
现在有一个[提案](https://github.com/tc39/proposal-do-expressions),使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上`do`,使它变为`do`表达式,然后就会返回内部最后执行的表达式的值。
```javascript
let x = do {
let t = f();
t * t + 1;
};
```
上面代码中,变量`x`会得到整个块级作用域的返回值(`t * t + 1`)。
`do`表达式的逻辑非常简单:封装的是什么,就会返回什么。
```javascript
// 等同于 <表达式>
do { <表达式>; }
// 等同于 <语句>
do { <语句> }
```
`do`表达式的好处是可以封装多个语句,让程序更加模块化,就像乐高积木那样一块块拼装起来。
```javascript
let x = do {
if (foo()) { f() }
else if (bar()) { g() }
else { h() }
};
```
上面代码的本质,就是根据函数`foo`的执行结果,调用不同的函数,将返回结果赋给变量`x`。使用`do`表达式,就将这个操作的意图表达得非常简洁清晰。而且,`do`块级作用域提供了单独的作用域,内部操作可以与全局作用域隔绝。
值得一提的是,`do`表达式在 JSX 语法中非常好用。
```javascript
return (
<nav>
<Home />
{
do {
if (loggedIn) {
<LogoutButton />
} else {
<LoginButton />
}
}
}
</nav>
)
```
上面代码中,如果不用`do`表达式,就只能用三元判断运算符(`?:`)。那样的话,一旦判断逻辑复杂,代码就会变得很不易读。
## throw 表达式
JavaScript 语法规定`throw`是一个命令,用来抛出错误,不能用于表达式之中。
```javascript
// 报错
console.log(throw new Error());
```
上面代码中,`console.log`的参数必须是一个表达式,如果是一个`throw`语句就会报错。
现在有一个[提案](https://github.com/tc39/proposal-throw-expressions),允许`throw`用于表达式。
```javascript
// 参数的默认值
function save(filename = throw new TypeError("Argument required")) {
}
// 箭头函数的返回值
lint(ast, {
with: () => throw new Error("avoid using 'with' statements.")
});
// 条件表达式
function getEncoder(encoding) {
const encoder = encoding === "utf8" ?
new UTF8Encoder() :
encoding === "utf16le" ?
new UTF16Encoder(false) :
encoding === "utf16be" ?
new UTF16Encoder(true) :
throw new Error("Unsupported encoding");
}
// 逻辑表达式
class Product {
get id() {
return this._id;
}
set id(value) {
this._id = value || throw new Error("Invalid value");
}
}
```
上面代码中,`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
```
## 直接输入 U+2028 和 U+2029
JavaScript 字符串允许直接输入字符,以及输入字符的转义形式。举例来说,“中”的 Unicode 码点是 U+4e2d你可以直接在字符串里面输入这个汉字也可以输入它的转义形式`\u4e2d`,两者是等价的。
```javascript
'中' === '\u4e2d' // true
```
但是JavaScript 规定有5个字符不能在字符串里面直接使用只能使用转义形式。
- U+005C反斜杠reverse solidus)
- U+000D回车carriage return
- U+2028行分隔符line separator
- U+2029段分隔符paragraph separator
- U+000A换行符line feed
举例来说,字符串里面不能直接包含反斜杠,一定要转义写成`\\`或者`\u005c`
这个规定本身没有问题,麻烦在于 JSON 格式允许字符串里面直接使用 U+2028行分隔符和 U+2029段分隔符。这样一来服务器输出的 JSON 被`JSON.parse`解析,就有可能直接报错。
JSON 格式已经冻结RFC 7159没法修改了。为了消除这个报错现在有一个[提案](https://github.com/tc39/proposal-json-superset),允许 JavaScript 字符串直接输入 U+2028行分隔符和 U+2029段分隔符
```javascript
const PS = eval("'\u2029'");
```
根据这个提案,上面的代码不会报错。
注意,模板字符串现在就允许直接输入这两个字符。另外,正则表达式依然不允许直接输入这两个字符,这是没有问题的,因为 JSON 本来就不允许直接包含正则表达式。