最新提案

1. do 表达式 (尚未实现)

本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。

{
  let t = f();
  t = t * t + 1;
}

上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到t的值,因为块级作用域不返回值,除非t是全局变量。

现在有一个提案,使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上do,使它变为do表达式,然后就会返回内部最后执行的表达式的值。

let x = do {
  let t = f();
  t * t + 1;
};

面代码中,变量x会得到整个块级作用域的返回值(t * t + 1)。

do表达式的逻辑非常简单:封装的是什么,就会返回什么。

// 等同于 <表达式>
do { <表达式>; }

// 等同于 <语句>
do { <语句> }

do表达式的好处是可以封装多个语句,让程序更加模块化,就像乐高积木那样一块块拼装起来。

值得一提的是,do表达式在 JSX 语法中非常好用。

2. throw 表达式(尚未实现)

JavaScript 语法规定throw是一个命令,用来抛出错误,不能用于表达式之中。

上面代码中,console.log的参数必须是一个表达式,如果是一个throw语句就会报错。

现在有一个提案,允许throw用于表达式。

3. 函数的部分执行(尚未实现)

1)语法

多参数的函数有时需要绑定其中的一个或多个参数,然后返回一个新函数。

上面代码中,add7函数其实是add函数的一个特殊版本,通过将一个参数绑定为7,就可以从add得到add7

上面两种写法都有些冗余。其中,bind方法的局限更加明显,它必须提供this,并且只能从前到后一个个绑定参数,无法只绑定非头部的参数。

现在有一个提案,使得绑定参数并返回一个新函数更加容易。这叫做函数的部分执行(partial application)。

根据新提案,?是单个参数的占位符,...是多个参数的占位符。以下的形式都属于函数的部分执行。

?...只能出现在函数的调用之中,并且会返回一个新函数。

2)注意点

  1. 函数的部分执行是基于原函数的。如果原函数发生变化,部分执行生成的新函数也会立即反映这种变化。

  1. 如果预先提供的那个值是一个表达式,那么这个表达式并不会在定义时求值,而是在每次调用时求值。

  1. 如果新函数的参数多于占位符的数量,那么多余的参数将被忽略。

写成下面这样,多余的参数就没有问题。

  1. ...只会被采集一次,如果函数的部分执行使用了多个...,那么每个...的值都将相同。

g定义了两个...占位符,真正执行的时候,它们的值是一样的。

4. 管道运算符(尚未实现)

JavaScript 的管道是一个运算符,写作|>。它的左边是一个表达式,右边是一个函数。管道运算符把左边表达式的值,传入右边的函数进行求值。

管道运算符最大的好处,就是可以把嵌套的函数,写成从左到右的链式表达式。

上面是三个简单的函数。如果要嵌套执行,传统的写法和管道的写法分别如下。

管道运算符只能传递一个值,这意味着它右边的函数必须是一个单参数函数。如果是多参数函数,就必须进行柯里化,改成单参数的版本。

管道运算符对于await函数也适用。

5. 数值分隔符(已实现)

欧美语言中,较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性。比如,1000可以写作1,000

现在有一个提案,允许 JavaScript 的数值使用下划线(_)作为分隔符。

JavaScript 的数值分隔符没有指定间隔的位数,也就是说,可以每三位添加一个分隔符,也可以每一位、每两位、每四位添加一个。

小数和科学计数法也可以使用数值分隔符。

数值分隔符有几个使用注意点。

  • 不能在数值的最前面(leading)或最后面(trailing)。

  • 不能两个或两个以上的分隔符连在一起。

  • 小数点的前后不能有分隔符。

  • 科学计数法里面,表示指数的eE前后不能有分隔符。

下面的写法都会报错:

除了十进制,其他进制的数值也可以使用分隔符。

注意,分隔符不能紧跟着进制的前缀0b0B0o0O0x0X

下面三个将字符串转成数值的函数,不支持数值分隔符。主要原因是提案的设计者认为,数值分隔符主要是为了编码时书写数值的方便,而不是为了处理外部输入的数据。

  • Number()

  • parseInt()

  • parseFloat()

6. Math.signbit()(尚未实现)

Math.sign()用来判断一个值的正负,但是如果参数是-0,它会返回-0

这导致对于判断符号位的正负,Math.sign()不是很有用。JavaScript 内部使用 64 位浮点数(国际标准 IEEE 754)表示数值,IEEE 754 规定第一位是符号位,0表示正数,1表示负数。所以会有两种零,+0是符号位为0时的零值,-0是符号位为1时的零值。实际编程中,判断一个值是+0还是-0非常麻烦,因为它们是相等的。

目前,有一个提案,引入了Math.signbit()方法判断一个数的符号位是否设置了。

可以看到,该方法正确返回了-0的符号位是设置了的。

该方法的算法如下:

  • 如果参数是NaN,返回false

  • 如果参数是-0,返回true

  • 如果参数是负值,返回true

  • 其他情况返回false

7. 双冒号运算符(尚未实现)

箭头函数可以绑定this对象,大大减少了显式绑定this对象的写法(callapplybind)。但是,箭头函数并不适用于所有场合,所以现在有一个提案,提出了“函数绑定”(function bind)运算符,用来取代callapplybind调用。

函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面。

如果双冒号运算符的运算结果,还是一个对象,就可以采用链式写法。

8. Realm API(尚未实现)

Realm API 提供沙箱功能(sandbox),允许隔离代码,防止那些被隔离的代码拿到全局对象。

以前,经常使用<iframe>作为沙箱。

上面代码中,`的全局对象是独立的(iframe.contentWindow`)。Realm API 可以取代这个功能。

上面代码中,Realm API单独提供了一个全局对象new Realm().global

Realm API 提供一个Realm()构造函数,用来生成一个 Realm 对象。该对象的global属性指向一个新的顶层对象,这个顶层对象跟原始的顶层对象类似。

下面的代码可以证明,Realm 顶层对象与原始顶层对象是两个对象。

Realm 沙箱里面只能运行 ECMAScript 语法提供的 API,不能运行宿主环境提供的 API。

上面代码中,Realm 沙箱里面没有console对象,导致报错。因为console不是语法标准,是宿主环境提供的。

如果要解决这个问题,可以使用下面的代码。

9. #! 命令

Unix 的命令行脚本都支持#!命令,又称为 Shebang 或 Hashbang。这个命令放在脚本的第一行,用来指定脚本的执行器。

Python 脚本的第一行。

现在有一个提案,为 JavaScript 脚本引入了#!命令,写在脚本文件或者模块文件的第一行。

有了这一行以后,Unix 命令行就可以直接执行脚本。

对于 JavaScript 引擎来说,会把#!理解成注释,忽略掉这一行。

10. import.meta

开发者使用一个模块时,有时需要知道模板本身的一些信息(比如模块的路径)。现在有一个提案,为 import 命令添加了一个元属性import.meta,返回当前模块的元信息。

import.meta只能在模块内部使用,如果在模块外部使用会报错。

这个属性返回一个对象,该对象的各种属性就是当前运行的脚本的元信息。具体包含哪些属性,标准没有规定,由各个运行环境自行决定。一般来说,import.meta至少会有下面两个属性。

1) import.meta.url

import.meta.url返回当前模块的 URL 路径。举例来说,当前模块主文件的路径是https://foo.com/main.jsimport.meta.url就返回这个路径。如果模块里面还有一个数据文件data.txt,那么就可以用下面的代码,获取这个数据文件的路径。

注意,Node.js 环境中,import.meta.url返回的总是本地路径,即是file:URL协议的字符串,比如file:///home/user/foo.js

2)import.meta.scriptElement

import.meta.scriptElement是浏览器特有的元属性,返回加载模块的那个<script>元素,相当于document.currentScript属性。

最后更新于

这有帮助吗?