# 最新提案 本章介绍一些尚未进入标准、但很有希望的最新提案。 ## 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 ( ) ``` 上面代码中,如果不用`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`表达式。