本期精读文章是：[TC39, ECMAScript, and the Future of JavaScript]( https://ponyfoo.com/articles/tc39-ecmascript-proposals-future-of-javascript)

# 1 引言

<img src="https://img.alicdn.com/imgextra/i3/O1CN01Z7A0st1Xx0Vu2Tnbv_!!6000000002989-2-tps-1000-820.png" alt="logo" width="500" />

觉得 es6 es7 动不动就加新特性很烦？提案的讨论已经放开了，每个人都可以做 js 的主人，赶快与我一起了解下有哪些特性在日程中！

# 2 内容概要

### TC39 是什么？包括哪些人？

一个推动 JavaScript 发展的委员会，由各个主流浏览器厂商的代表构成。

### 为什么会出现这样一个组织？

从标准到落地是一个漫长的过程，相信大家上次阅读 web components 就能体会到标准到浏览器支持是一个漫长的过程。

### TC39 这群人主要的工作是什么？

制定 ECMAScript 标准，标准生成的流程，并实现。

### 标准的流程是什么样的？

包括五个步骤：

- stage0 `strawman`

任何讨论、想法、改变或者还没加到提案的特性都在这个阶段。只有 TC39 成员可以提交。

- stage1 `proposal`
（1）产出一个正式的提案。
（2）发现潜在的问题，例如与其他特性的关系，实现难题。
（3）提案包括详细的 API 描述，使用例子，以及关于相关的语义和算法。

- stage2 `draft`
（1）提供一个初始的草案规范，与最终标准中包含的特性不会有太大差别。草案之后，原则上只接受增量修改。
（2）开始实验如何实现，实现形式包括 polyfill, 实现引擎（提供草案执行本地支持），或者编译转换（例如 babel）

- stage3 `candidate`
（1）候选阶段，获得具体实现和用户的反馈。此后，只有在实现和使用过程中出现了重大问题才会修改。
（1）规范文档必须是完整的，评审人和 ECMAScript 的编辑要在规范上签字。
（2）至少要在一个浏览器中实现，提供 polyfill 或者 babel 插件。

- stage4 `finished`
（1）已经准备就绪，该特性会出现在下个版本的 ECMAScript 规范之中。。
（2）需要通过有 2 个独立的实现并通过验收测试，以获取使用过程中的重要实践经验。

### 一般可以去哪里查看 TC39 标准的进程呢？

stage0 的提案 https://github.com/tc39/proposals/blob/master/stage-0-proposals.md
stage1 - 4 的提案 https://github.com/tc39/proposals

### 我们怎么在程序中应用这些新特性呢？

babel 的插件：`babel-presets-stage-0` `babel-presets-stage-1` `babel-presets-stage-2` `babel-presets-stage-3` `babel-presets-stage-4`

# 3 精读

本次提出独到观点的同学有：
[@huxiaoyun](https://github.com/huxiaoyun) [@monkingxue](https://github.com/monkingxue) [@jasonslyvia](https://github.com/jasonslyvia) [@ascoders](https://github.com/ascoders)，精读由此归纳。

## 3.1 Stage 4 大家庭

### [Array.prototype.includes](https://github.com/tc39/Array.prototype.includes/)

```javascript
assert([1, 2, 3].includes(2) === true);
assert([1, 2, 3].includes(4) === false);

assert([1, 2, NaN].includes(NaN) === true);

assert([1, 2, -0].includes(+0) === true);
assert([1, 2, +0].includes(-0) === true);

assert(["a", "b", "c"].includes("a") === true);
assert(["a", "b", "c"].includes("a", 1) === false);
```

这个 api 很方便，没有悬念的进入了草案中。

曾争议过是否使用 Array.prototype.contains，但由于 [不兼容因素](https://esdiscuss.org/topic/having-a-non-enumerable-array-prototype-contains-may-not-be-web-compatible) 而换成了 includes。

### [Exponentiation operator](https://github.com/rwaldron/exponentiation-operator)

```javascript
// x ** y

let squared = 2 ** 2;
// same as: 2 * 2

let cubed = 2 ** 3;
// same as: 2 * 2 * 2
```

列表中进入了 stage4，但其 git 仓库 readme 还停留在 stage3。。

虽然已经有 `Math.pow` 了，但由于其他语言都支持此方式，js 也就支持了。

### [Object.values/Object.entries](https://github.com/tc39/proposal-object-values-entries)

```javascript
Object.values({
	a: 1,
	b: 2,
	c: Symbol(),
}) // [1, 2, Symbol()]

Object.entries({
	a: 1,
	b: 2,
	c: Symbol(),
}) // [["a", 1], ["b", 2], ["c", Symbol()]]
```

也没有什么争议，Object.keys 都有了，获取 values、entries 也是合理的。

TC39 会议中有争辩过为何不返回迭代器，原因挺有意思，因为 Object.keys 返回的是数组，所以这两个 api 还是与老大哥统一吧。

### [String.prototype.padStart / String.prototype.padEnd](https://github.com/tc39/proposal-string-pad-start-end)

```javascript
"foo".padStart(5, "bar") // bafoo
"foo".padEnd(5, "bar") // fooba
```

解决了字符串补齐需求，很棒！

### [Object.getOwnPropertyDescriptors](https://github.com/tc39/proposal-object-getownpropertydescriptors)

```javascript
Object.getOwnPropertyDescriptors({ a: 1})
// { a: {
// 	  configurable: true,
// 	  enumberable: true,
// 	  value: 1,
//	  writable: true
// } }
```

特别是 babel 与 typescript 处理 class property decorator 方式不同的时候（typescript 处理得更成熟一些），会导致 babel 处理装饰器时，成员变量不设置默认值时，configurable 默认为 false，通过这个函数检查变量的配置很方便。

### [Trailing commas in function parameter lists and calls](https://github.com/tc39/proposal-trailing-function-commas)

```javascript
function clownPuppiesEverywhere(
   param1,
   param2, // Next parameter that's added only has to add a new line, not modify this line
 ) { /* ... */ }
```

js 终于原生支持了，以前不支持的时候多加逗号还会报错，需要预编译工具删除最后一个逗号，现在终于名正言顺了。

### [Async functions](https://github.com/tc39/ecmascript-asyncawait)

这个不用多说了，都说好用。

### [Shared memory and atomics](https://github.com/tc39/ecmascript_sharedmem)

这是 ECMAScript 共享内存与 Atomics 的规范，涉及内容非常多，主要涉及到 asm.js。

asm.js 是一种性能解决方案，比如可以定义一个精确的 64k 堆：

```javascript
var heap = new ArrayBuffer( 0x10000 )
```

### [Lifting template literal restriction](https://github.com/tc39/proposal-template-literal-revision)

```javascript
styled.div`
  background-color: red;
`
```

`styled.div = text => {}` 就可以处理了，目前使用最多在 styled-components 库里，这种场景还是蛮方便的。

## 3.2 Stage 3 大家庭

### [Function.prototype.toString revision](https://github.com/tc39/Function-prototype-toString-revision)

对函数的 toString 规则进行了修改：http://tc39.github.io/Function-prototype-toString-revision/#sec-function.prototype.tostring

当调用内置函数或 `.bind` 后函数，toString 方法会返回 [NativeFunction](http://tc39.github.io/Function-prototype-toString-revision/#prod-NativeFunction)。

### [global](https://github.com/tc39/proposal-global)

为 ECMAScript 规范添加 `global` 变量，同构代码再也不用这么写了：

```javascript
var getGlobal = function () {
	// the only reliable means to get the global object is
	// `Function('return this')()`
	// However, this causes CSP violations in Chrome apps.
	if (typeof self !== 'undefined') { return self; }
	if (typeof window !== 'undefined') { return window; }
	if (typeof global !== 'undefined') { return global; }
	throw new Error('unable to locate global object');
};
```

虽然前端环境与 nodejs 区别很大，但既然提案进入了 stage3，说明大家非常关注 js 整体的生态，只要整体方向良性发展，相信不久将会进入 stage4。

### [Rest/Spread Properties](https://github.com/tc39/proposal-object-rest-spread)

```javascript
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x; // 1
y; // 2
z; // { a: 3, b: 4 }
```

不得不说，非常常用，而且 [babel](https://babeljs.io/docs/plugins/transform-object-rest-spread/)，[jsTransform](https://github.com/facebookarchive/jstransform)，[typescript](https://github.com/Microsoft/TypeScript) 均支持，感觉很快会进入 stage4.

### [Asynchronous Iteration](https://github.com/tc39/proposal-async-iteration)

```javascript
const { value, done } = syncIterator.next();

asyncIterator.next().then(({ value, done }) => /* ... */);
```

```javascript
for await (const line of readLines(filePath)) {
  console.log(line);
}
```

```javascript
async function* readLines(path) {
  let file = await fileOpen(path);

  try {
    while (!file.EOF) {
      yield await file.readLine();
    }
  } finally {
    await file.close();
  }
}
```

异步迭代器实现了 async await 与 generator 的结合。
然而 async await 是使用 generator 的语法糖，generator 也可以通过 switch 等流程控制函数模拟。更重要的是异步在 generator 中本身就可以实现，我在[《Callback Promise Generator Async-Await 和异常处理的演进》](https://github.com/ascoders/blog/issues/14) 文章中提过。

语法的修改一定不能为了方便（在 ECMAScript 中可能出现），但这种混杂的方式容易让人混淆 await 与 generator 之间的关系，是否进入 stage4 还需仔细斟酌。

### [import()](https://github.com/tc39/proposal-dynamic-import)

```javascript
import(`./section-modules/${link.dataset.entryModule}.js`)
    .then(module => {
      module.loadPageInto(main);
    })
    .catch(err => {
      main.textContent = err.message;
    });
```

这个提案主要增加了函数调用版的 import，而 webpack 等构建工具也在积极实现此规范，并作为动态加载的最佳范例。希望这种“官方 Amd”可以早日加入草案。

### [RegExp Lookbehind Assertions](https://github.com/tc39/proposal-regexp-lookbehind)

javascript 正则表达式一直不支持后行断言，不过现在已经进入 stage3，相信不久会进入 stage4.

前向断言：

```javascript
/\d+(?=%)/.exec("100% of US presidents have been male") // ["100"]
/\d+(?!%)/.exec("that’s all 44 of them") // ["44"]
```

后向断言：

```javascript
/(?<=\$)\d+/.exec("Benjamin Franklin is on the $100 bill")  // ["100"]
/(?<!\$)\d+/.exec("it’s is worth about €90")                // ["90"]
```

后向断言会获取某个字符后面跟的内容，在获取美刀等货币单位上有很大用途。chrome 可以使用 `chrome.exe --js-flags="--harmony-regexp-lookbehind"` 命令开启。

### [RegExp Unicode Property Escapes](https://github.com/tc39/proposal-regexp-unicode-property-escapes)

```javascript
const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π');
// → true
```

以上 `π` 字符是一个希腊字符，通过指定 `\p{Script=Greek}` 就可以匹配这个字符了！

虽然可以通过引用希腊字符（或者其他编码）表做正则处理，当每当更新表时，更新起来会非常麻烦，不如让浏览器原生支持 `\p{UnicodePropertyName=UnicodePropertyValue}` 的正则语法，帮助开发人员解决这个烦恼。

### [RegExp named capture groups](https://github.com/tc39/proposal-regexp-named-groups)

```javascript
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2015-01-02');
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';
```

```javascript
let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
console.log(`one: ${one}, two: ${two}`);  // prints one: foo, two: bar
```

同时，还支持 **反向引用能力**，可以通过 `\k<name>` 的语法，在正则中表示同一种匹配类型，这个和 ts 范型很像：

```javascript
let duplicate = /^(?<half>.*).\k<half>$/u;
duplicate.test('a*b'); // false
duplicate.test('a*a'); // true
```

总体来看非常给力，毫无意义的下标也是正则反人类的原因之一，这个提案通过的话，正则会变得更加可读。

### [s (dotAll) flag for regular expressions](https://github.com/tc39/proposals)

```javascript
/foo.bar/s.test('foo\nbar');
// → true
```

通过添加了新的标识符 `/s`，表示 `.` 这个标志可以匹配任何值。原因是觉得现在正则的做法比较反人类：

```javascript
/foo[^]bar/.test('foo\nbar');
// → true
/foo[\s\S]bar/.test('foo\nbar');
// → true
```

从保守派角度来看，可能因为掌握了 `[^]` `[\s\S]` 这种奇技淫巧而沾沾自喜，借此提高正则的门槛，让初学者“看不懂”，而高级语言的第一要义是可读性，`RegExp Unicode Property Escapes` 与 `RegExp named capture groups` 进入草案就是表明了对正则语义化改进的决心，相信这个提案也会被采纳。

### [Legacy RegExp features in JavaScript](https://github.com/tc39/proposal-regexp-legacy-features)

该提案主要针对 RegExp 遗留的静态属性进行梳理。平时很少接触，希望了解的人解读一下。

## 3.3 Stage2 大家庭

### [function.sent metaproperty](https://github.com/allenwb/ESideas/blob/master/Generator%20metaproperty.md)

generator 的第一个 `.next` 参数会被抛弃，因为第一次 next 没有对应上任何 `yield`，如下代码就会产生疑惑：

```javascript
function *adder(total=0) {
   let increment=1;
   while (true) {
       switch (request = yield total += increment) {
          case undefined: break;
          case "done": return total;
          default: increment = Number(request);
       }
   }
}

let tally = adder();
tally.next(0.1); // argument will be ignored
tally.next(0.1);
tally.next(0.1);
let last=tally.next("done");
console.log(last.value);  //1.2 instead of 0.3
```

当引入 `function.sent` 后，可以接收来自 next 的传值，**包括初始传值**：

```javascript
function *adder(total=0) {
   let increment=1;
   do {
       switch (request = function.sent){
          case undefined: break;
          case "done": return total;
          default: increment = Number(request);
       }
       yield total += increment;
   } while (true)
}

let tally = adder();
tally.next(0.1); // argument no longer ignored
tally.next(0.1);
tally.next(0.1);
let last=tally.next("done");
console.log(last.value);  //0.3
```

这是个很棒的特性，也不存在语意兼容问题，但 api 还是比较怪，而且自此 yield 接收参数也变得没有意义，况且如今 async await 逐渐成为主流，这种修正没有强烈刚需。而且 yield 的语意本身没有错误，这个提案比较危险。

### [String.prototype.{trimStart,trimEnd}](https://github.com/tc39/proposal-string-left-right-trim)

既然 `padStart` 与 `padEnd` 都进入了 stage4，`trimStart` `trimEnd` 这两个 api 也非常常用，而且从 ES5 将 `String.prototype.trim` 引入了标准来看，这两个非常有望晋升到 stage3。

### [Class Fields](https://github.com/tc39/proposal-class-fields)

```javascript
class Counter extends HTMLElement {
  x = 0;
  #y = 1;
}
```

类成员变量，有了它 js 就完整了。虽然觉得似有变量符号很难看，但成员变量绝对是非常有用的语法，在 react 中已经很常用了：

```javascript
class Todo extends React.Component {
  state = { //.. }
}
```

### [Promise.prototype.finally](https://github.com/tc39/proposal-promise-finally)

就像 `try/catch/finally` 一样，try return 了都能执行 finally，是非常方便的，对 promise 来说也是如此，[bluebird](https://github.com/petkaantonov/bluebird) [Q](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) 等库已经实现了此功能。
 
但是库实现不足以使其纳入标准，只有当这些需求足够常用和通用时才会考虑。第三方库可能从竞争力角度考虑，多支持一种功能、少些一行代码就是多一份筹码，但语言规范是不能在乎这些的。

### [Class and Property Decorators](http://tc39.github.io/proposal-decorators/)

类级别的装饰器已经进入 stage2 了，但现代前端开发中已经非常常用，很可能会进一步进入 stage3.

如果这个提案被废弃，那么大部分现代 js 代码将面临大量使用不存在语法的窘境。不过乐观的是，目前还找不到更好的装饰器替代方案，而在 python 中也存在装饰器模式可以参考。

### [Intl.Segmenter](https://github.com/tc39/proposal-intl-segmenter)

```javascript
// Create a segmenter in your locale
let segmenter = new Intl.Segmenter("fr", {granularity: "word"});

// Get an iterator over a string
let iterator = segmenter.segment("Ceci n'est pas une pipe");

// Iterate over it!
for (let {segment, breakType} of iterator) {
  console.log(`segment: ${segment} breakType: ${breakType}`);
  break;
}

// logs the following to the console:
// segment: Ceci breakType: letter
```

`Intl.Segmenter` 可以帮助分析单词断句分析，可能在 nlp 领域比较有用，在文本编辑器自动选中功能中也很有用。

虽然不是刚需，但 js 作为网页交互的语言，确实需要解决分析用户输入的问题。

### [Arbitrary-precision Integers](https://github.com/tc39/proposal-bigint)

新增了基本类型：整数类型，以及 Integer api 与字面语法 1234n。

目前 js 使用 64 位浮点数处理所有计算，直接导致了运算效率低下，这个提案弥补了 js 的计算缺点，希望可以早日进入草案。

提案名称由 Integer 改为 BigInt。

### [import.meta](https://github.com/tc39/proposals)

提出了使用 `import.meta` 获取当前模块的域信息。类比 nodejs 存在 `__dirname` 等信息标志当前脚本信息，通过浏览器加载的模块也应当拥有这种能力。

目前 js 可以通过如下方式获取脚本信息：

```javascript
const theOption = document.currentScript.dataset.option;
```

这样污染了全局变量，脚本信息应当存储在脚本作用域中，因此提案希望将脚本信息存储在脚本的 `import.meta`  变量中，因此可以这么使用：

```javascript
(async () => {
  const response = await fetch(new URL("../hamsters.jpg", import.meta.url));
  const blob = await response.blob();

  const size = import.meta.scriptElement.dataset.size || 300;

  const image = new Image();
  image.src = URL.createObjectURL(blob);
  image.width = image.height = size;

  document.body.appendChild(image);
})();
```

## 3.4 Stage1 大家庭

### [Date.parse fallback semantics](https://github.com/FaustDeGoethe/proposal-date-time-string-format)

通过字符串格式化日期一直是跨浏览器的痛点，本提案希望通过新增 `Date.parse` 标准完成这个功能。

> "The function first attempts to parse the format of the String according to the rules 
> (including extended years) called out in Date Time String Format (20.3.1.16). If the 
> String does not conform to that format the function may fall back to any 
> implementation-specific heuristics or implementation-specific date formats."

正如提案所说，“如果字符串不满足  ISO 8601 格式，可以返回你想返回的任何值” 这样迷惑开发者是没有任何意义的，这样只会让开发者越来越不相信 js 是跨平台的语言。

这么重要的规范居然才 stage1，必须要顶上去。

### [export * as ns from "mod"; statements](https://github.com/tc39/proposal-export-ns-from)

```javascript
export * as someIdentifier from "someModule";
```

很方便的 api，很多时候希望导出某个模块的全部接口，又不希望命名冲突，可以少写一行 import。


### [export v from "mod"; statements](https://github.com/tc39/proposal-export-default-from)

这个提案与 [export * as ns from "mod"; statements](https://github.com/tc39/proposal-export-ns-from)
 冲突了，感觉 [export * as ns from "mod"; statements](https://github.com/tc39/proposal-export-ns-from)
 提案更清晰一些。

### [Observable](https://github.com/tc39/proposal-observable)

可观察类型可以从 dom 事件、轮询等触发事件中创建监听并订阅：

```javascript
function listen(element, eventName) {
    return new Observable(observer => {
        // Create an event handler which sends data to the sink
        let handler = event => observer.next(event);

        // Attach the event handler
        element.addEventListener(eventName, handler, true);

        // Return a cleanup function which will cancel the event stream
        return () => {
            // Detach the event handler from the element
            element.removeEventListener(eventName, handler, true);
        };
    });
}

// Return an observable of special key down commands
function commandKeys(element) {
    let keyCommands = { "38": "up", "40": "down" };

    return listen(element, "keydown")
        .filter(event => event.keyCode in keyCommands)
        .map(event => keyCommands[event.keyCode])
}

let subscription = commandKeys(inputElement).subscribe({
    next(val) { console.log("Received key command: " + val) },
    error(err) { console.log("Received an error: " + err) },
    complete() { console.log("Stream complete") },
});
```

这个名字和 Object.observe 很像，不过没什么关系。该功能已经被 [RxJS](https://github.com/ReactiveX/RxJS)、[XStream](https://github.com/staltz/xstream) 等库实现。

### [String#matchAll](https://github.com/tc39/String.prototype.matchAll)

目前正则表达式想要匹配全部的语法不够语义化，提案希望通过 `matchAll` 返回迭代器来遍历匹配结果，很赞！

现在匹配全部只能使用 `while ((result = patt.exec(str)) != null)` 这种方式遍历，不优雅。

### [WeakRefs](https://github.com/tc39/proposal-weakrefs)

弱引用，提案地址文档：https://github.com/tc39/proposal-weakrefs/blob/master/specs/Weak%20References%20for%20EcmaScript.pdf

有点像 OC 的弱引用，当对象被释放时，当前持有弱引用的对象也会被 GC 回收，但似乎还没有开始讨论，js 越来越底层了？

### [Frozen Realms](https://github.com/FUDCo/frozen-realms)

增强了 [Realms](https://github.com/tc39/proposal-realms) 提案，利用不可变结构，实现结构共享。

### [Math Extensions](https://github.com/rwaldron/proposal-math-extensions)

Math 函数的拓展包含的函数：https://rwaldron.github.io/proposal-math-extensions/

这个函数拓展很给力，特别是设计游戏，计算角度的时候：

```javascript
Math.DEG_PER_RAD // Math.PI / 180
```

`Math.DEG_PER_RAD` 是一种单位，让角度可以用 0～360 为周期的数字表示，比如射击子弹时的角度、或者做可视化时都非常有用，类比 css 中的：`transform: rotate(180deg);`。

### [of and from on collection constructors](https://github.com/tc39/proposal-setmap-offrom)

该提案设计了 Set、Map 类型的 `of` `from` 方法，具体见此：https://tc39.github.io/proposal-setmap-offrom/

问题由于:

```javascript
Reflect.construct(Array, [1,2,3]) // [1,2,3]
Reflect.construct(Set, [1,2,3]) // Uncaught TypeError: undefined is not a function
```

因为 Set 接收的参数是数组，而 construct 会调用 `CreateListFromArrayLike` 将参数打平，变成了 `new Set(1, 2, 3)` 传入，实际上是语法错误的，因此作者提议新增下 Set、Map 的 `of` `from` 方法。

Set、Map 在国内环境用的比较少，也很少有人计较这个问题，不过从技术角度来看，确实需要修复。。

### [Generator arrow functions (=>*)](https://esdiscuss.org/topic/generator-arrow-functions)

还是挺有必要的，毕竟都出箭头函数了，也要支持一下箭头函数的 generator 语法。

### [Promise.try](https://github.com/tc39/proposal-promise-try)

同理，各大库都有实现，好处是所有错误都可以通过 `.catch` 捕获，而不用担心同步、异步错误的抛出。

### [Null Propagation](https://docs.google.com/presentation/d/11O_wIBBbZgE1bMVRJI8kGnmC6dWCBOwutbN9SWOK0fU/view#slide=id.p)

超级有用，看代码就知道了：

```javascript
const firstName = message.body?.user?.firstName || 'default'
```

该功能完全等同：

```javascript
const firstName = 
	(message && 
	message.body && 
	message.body.user &&
	message.body.user.firstName) || 'default'
```

希望立刻进入 stage4.

### [Math.signbit: IEEE-754 sign bit](http://jfbastien.github.io/papers/Math.signbit.html)

当值为 负数 或 -0 时返回 `true`。由于 `Math.sign` 不区分 +0 与 -0，因此提案建议增加此函数，而且此函数在 c、c++、go 语言都有实现。

### [Error stacks](https://github.com/tc39/proposal-error-stacks)

提案建议将 `Error.prototype.stack` 作为标准，这对错误上报与分析特别有用，强烈支持。

### [do expressions](https://gist.github.com/dherman/1c97dfb25179fa34a41b5fff040f9879)

```javascript
return (
  <nav>
    <Home />
    {
      do {
        if (loggedIn) {
          <LogoutButton />
        } else {
          <LoginButton />
        }
      }
    }
  </nav>
)
```

`jsx` 再也不用写得超长了，`styled-components` 中被诟病的分支判断难以阅读的问题也会烟消云散，因为我们有 `do`!

### [Realms](https://github.com/tc39/proposal-realms)

```javascript
let realm = new Realm();

let outerGlobal = window;
let innerGlobal = realm.global;

let f = realm.evalScript("(function() { return 17 })");

f() === 17 // true

Reflect.getPrototypeOf(f) === outerGlobal.Function.prototype // false
Reflect.getPrototypeOf(f) === innerGlobal.Function.prototype // true
```

`Realms` 提供了 global 环境的隔离，eval 执行代码时不再会污染全局，简直是测试的福利，脑洞很大。

### [Temporal](https://github.com/maggiepint/proposal-temporal)

与 `Date` 类似，但功能更强：

```javascript
var ldt = new temporal.LocalDateTime(2017, 12, 31, 23, 59);
var ldt = new temporal.LocalDateTime(2017, 12, 31, 23, 59, options);
var ldt = new temporal.LocalDateTime(2017, 12, 31, 23, 59, 59);
var ldt = new temporal.LocalDateTime(2017, 12, 31, 23, 59, 59, options);
var ldt = new temporal.LocalDateTime(2017, 12, 31, 23, 59, 59, 123);
var ldt = new temporal.LocalDateTime(2017, 12, 31, 23, 59, 59, 123, options);
var ldt = new temporal.LocalDateTime(2017, 12, 31, 23, 59, 59, 123, 456789);
var ldt = new temporal.LocalDateTime(2017, 12, 31, 23, 59, 59, 123, 456789, options);

// add/subtract time  (Dec 31 2017 23:00 + 2h = Jan 1 2018 01:00)
var addHours = new temporal.LocalDateTime(2017, 12, 31, 23, 00).add(2, 'hours');

// add/subtract months  (Mar 31 - 1M = Feb 28)
var addMonths = new temporal.LocalDateTime(2017,03,31).subtract(1, 'months'); 

// add/subtract years  (Feb 29 2020 - 1Y = Feb 28 2019)
var subtractYears = new temporal.LocalDateTime(2020, 02, 29).subtract(1, 'years');
```

还自带时区转换 api 等等，如果进入草案，可以放弃 moment 这个重量级库了。

### [Float16 on TypedArrays, DataView, Math.hfround](https://docs.google.com/presentation/d/1Ta_IbravBUOvu7LUhlN49SvLU-8G8bIQnsS08P3Z4vY/edit?usp=sharing)

由于大多数 WebGL 纹理需要半精度以上的浮点数计算，推荐了 4 个 api：

- Float16Array
- DataView.prototype.getFloat16
- DataView.prototype.setFloat16
- Math.hfround(x)

### [Atomics.waitNonblocking](https://github.com/lars-t-hansen/moz-sandbox/blob/master/sab/waitNonblocking.md)

```javascript
var sab = new SharedArrayBuffer(4096);
var ia = new Int32Array(sab);
ia[37] = 0x1337;
test1();

function test1() {
  Atomics.waitNonblocking(ia, 37, 0x1337, 1000).then(function (r) { console.log("Resolved: " + r); test2(); });
}
var code = `
var ia = null;
onmessage = function (ev) {
  if (!ia) {
    console.log("Aux worker is running");
    ia = new Int32Array(ev.data);
  }
  console.log("Aux worker is sleeping for a little bit");
  setTimeout(function () { console.log("Aux worker is waking"); Atomics.wake(ia, 37); }, 1000);
}`;
function test2() {
  var w = new Worker("data:application/javascript," + encodeURIComponent(code));
  w.postMessage(sab);
  Atomics.waitNonblocking(ia, 37, 0x1337).then(function (r) { console.log("Resolved: " + r); test3(w); });
}
function test3(w) {
  w.postMessage(false);
  Atomics.waitNonblocking(ia, 37, 0x1337).then(function (r) { console.log("Resolved 1: " + r); });
  Atomics.waitNonblocking(ia, 37, 0x1337).then(function (r) { console.log("Resolved 2: " + r); });
  Atomics.waitNonblocking(ia, 37, 0x1337).then(function (r) { console.log("Resolved 3: " + r); });
  
}
```

该 api 可以在多线程操作中，有顺序的操作同一个内存地址，如上代码变量 `ia` 虽然在多线程中执行，但每个线程都会等资源释放后再继续执行。

### [Numeric separators](https://github.com/tc39/proposal-numeric-separator)

```javascript
1_000_000_000           // Ah, so a billion
101_475_938.38          // And this is hundreds of millions

let fee = 123_00;       // $123 (12300 cents, apparently)
let fee = 12_300;       // $12,300 (woah, that fee!)
let amount = 12345_00;  // 12,345 (1234500 cents, apparently)
let amount = 123_4500;  // 123.45 (4-fixed financial)
let amount = 1_234_500; // 1,234,500
```

提案希望 js 支持分隔符使大数字阅读性更好（不影响计算），很多语言都有实现，很人性化。

# 4 总结

每个草案都觉得很靠谱，涉及语义化、无障碍、性能、拓展语法、连接 nodejs 等方面，虽然部分提案[从语言设计角度是错误的](http://www.yinwang.org/blog-cn/2013/04/18/language-design-mistake2)，但 js 运行在网页端，涉及到人机交互、网络加载等问题，遇到的问题自然比任何语言都要复杂，每个提案都是从实践中出发，相信这种道路是正确的。

由于篇幅与时间限制，stage0 的提案等下次再讨论。特别提一点，stage0 的 [Cancellation API](https://github.com/tc39/proposal-cancellation) 很值得大家关注，取消异步操作是人心所向，大势所趋啊。

感谢所有参与讨论的同学，你们的支持会转化为我们的动力，每周更新，风雨无阻。

> 讨论地址是：[精读《TC39, ECMAScript, and the Future of JavaScript》 · Issue #21 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/21)

> 如果你想参与讨论，请[点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，每周五发布。

> 访问 [原始文章地址](https://github.com/dt-fe/weekly/blob/master/15.%E7%B2%BE%E8%AF%BB%20TC39%20%E4%B8%8E%20ECMAScript%20%E6%8F%90%E6%A1%88.md) , 获得更好阅读效果。
