前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

ES2017

作者头像
ayqy贾杰
发布2019-06-12 15:00:57
7670
发布2019-06-12 15:00:57
举报
文章被收录于专栏:黯羽轻扬黯羽轻扬

一.特性概览

2个主要特性:

  • Async functions
  • Shared memory and atomics

4个小特性:

  • Object.values/Object.entries
  • Object.getOwnPropertyDescriptors
  • String padding
  • Trailing commas in function parameter lists and calls

二.Async functions

一个里程碑式的特性,标志着JS异步编程体验上升到了一个新高度,具体见从Generator到Async function

三.Shared memory and atomics

算是在多线程并行能力方面的基础建设,分为2部分:

  • SharedArrayBuffer允许主线程、及WebWorkers之间共享数据
  • Atomic operations(原子操作)用来解决数据同步的问题,如加锁、事务

例如:

代码语言:javascript
复制
// 主线程
var w = new Worker("myworker.js");
var sab = new SharedArrayBuffer(1024);  // 1KiB shared memory
// 同样通过postMessage给worker线程丢过去
w.postMessage(sab);// worker线程(myworker.js)
var sab;
onmessage = function (ev) {
 sab = ev.data;  // 1KiB shared memory, the same memory as in the parent
}

之前

线程之间传递的是值copy,而不是共享引用

现在可以通过SharedArrayBuffer共享同一份数据,并且在worker线程里也可以创建共享数据:

Memory can be created in any agent and then shared with any other agent, and can be shared among many agents simultaneously.

另外,SharedArrayBuffer可以作为ArrayBuffer使用,所以也可以共享TypedArray:

代码语言:javascript
复制
var sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 100000); // 100000 primes
var ia = new Int32Array(sab);  // ia.length == 100000
var primes = new PrimeGenerator();
for ( let i=0 ; i < ia.length ; i++ )
 ia[i] = primes.next();
w.postMessage(ia);

由于数据是多线程共享的,势必面临数据同步的问题,通过Atomics全局对象提供的一些方法来解决:

代码语言:javascript
复制
// 读
Atomics.load(typedArray, index)
// 写
Atomics.store(typedArray, index, value)
// 写,返回旧值
Atomics.exchange(array, index, value)
// 条件写,仅当旧值等于oldval时才写,返回旧值
compareExchange(array, index, oldval, newval)
// 带读写锁的运算(加、减、与、或、异或)
Atomics.add(array, index, value)
Atomics.sub(array, index, value)
Atomics.and(array, index, value)
Atomics.or(array, index, value)
Atomics.xor(array, index, value)

这些原子操作不会被打断(not interruptible),在此基础上可以实现:

  • 保证连续读写操作的顺序
  • 避免写操作“丢失”(比如写到脏数据上了)

此外,还允许挂起/唤醒(更友好的线程等待方式,不多占资源):

代码语言:javascript
复制
Atomics.wait(typedArray, index, value[, timeout])
Atomics.wake(typedArray, index, count)

例如:

代码语言:javascript
复制
// A线程写
console.log(ia[37]);  // Prints 163
Atomics.store(ia, 37, 123456);
Atomics.wake(ia, 37, 1);// B线程等着读
Atomics.wait(ia, 37, 163);
console.log(ia[37]);  // Prints 123456

而不需要靠死循环来实现阻塞式等待:

代码语言:javascript
复制
while (Atomics.load(ia, 37) == 163);
console.log(ia[37]);  // Prints 123456

P.S.有意思的一点,主线程不允许挂起:

The specification allows the browser to deny wait on the main thread, and it is expected that most browsers will eventually do so. A denied wait throws an exception.

P.S.关于Shared memory and atomics特性的更多信息,请查看:

  • Shared memory – a brief tutorial
  • ES proposal: Shared memory and atomics

四.小特性

Object.values/Object.entries

代码语言:javascript
复制
// 返回 (1)自身的 (2)可枚举的 (3)非Symbol类型的 属性的值
Object.values(obj)

polyfill实现大致如下:

代码语言:javascript
复制
function values(obj) {
 var vals = [];
 for (var key in obj) {
   if (obj.hasOwnProperty(key) && obj.propertyIsEnumerable(key)) {
     vals.push(obj[key]);
   }
 }
 return vals;
}

Object.keys()一致,对属性都有3个限定条件(own && enumerable && non-Symbol-only)。因此,不考虑性能的话,可以实现更简单的polyfill:

代码语言:javascript
复制
function values(obj) {
 return Object.keys(obj).map(key => obj[key]);
}

类似的,还提供了:

代码语言:javascript
复制
// 返回 (1)自身的 (2)可枚举的 (3)非Symbol类型的 属性的键值对儿
Object.entries(obj)

polyfill也类似:

代码语言:javascript
复制
function entries(obj) {
 var entrys = [];
 for (var key in obj) {
   if (obj.hasOwnProperty(obj, key) && obj.propertyIsEnumerable(obj, key)) {
     entrys.push([key, obj[key]]);
   }
 }
 return entrys;
};

除了返回值形式不同以外,与Object.values(obj)一毛一样

应用场景上,Object.entries(obj)可以用来完成mapObject转Map的工作:

代码语言:javascript
复制
new Map(Object.entries({
   one: 1,
   two: 2,
}))
// 输出 Map(2) {"one" => 1, "two" => 2}
枚举性,原型属性与Symbol
  • 枚举性:通过obj.propertyIsEnumerable(key)来检查,下面用enumerable表示可枚举
  • 是不是原型属性:通过obj.hasOwnProperty(key)来检查,下面用own表示仅针对非原型属性
  • 是不是Symbol:通过typeof key === 'symbol'来检查,下面用non-Symbol-only表示仅针对非Symbol类型属性,用Symbol-only表示仅针对Symbol类型属性

JS里围绕对象属性的这3个特点提供了很多工具方法,除了上面提到的Object.keys()Object.values()Object.entries()外,还有:

  • Object.getOwnPropertyNames(obj):own && non-Symbol-only
  • Object.getOwnPropertySymbols():own && Symbol-only
  • Reflect.ownKeys(obj):own。等价于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

以及1种遍历方式:

  • for…in:enumerable && non-Symbol-only

P.S.想起了for…of?这个东西与对象关系不大,仅针对iterable,如类数组对象(arguments、DOMNodeList等)

Object.getOwnPropertyDescriptors

代码语言:javascript
复制
// 以对象字典形式返回 (1)自身的 所有属性的描述符
Object.getOwnPropertyDescriptors(obj)

包括Symbol类型属性与不可枚举属性,例如:

代码语言:javascript
复制
const obj = {
 [Symbol('foo')]: 123
};
Object.defineProperty(obj, 'bar', {
 value: 42,
 enumerable: false
});
console.log(Object.getOwnPropertyDescriptors(obj));
// 输出
// {
//   bar: {value: 42, writable: false, enumerable: false, configurable: false},
//   Symbol(foo): {value: 123, writable: true, enumerable: true, configurable: true}
// }
// 而 Object.keys(obj).length === 0

可以通过Reflect.ownKeys(obj)实现polyfill:

代码语言:javascript
复制
function getOwnPropertyDescriptors(obj) {
   const result = {};
   for (let key of Reflect.ownKeys(obj)) {
       result[key] = Object.getOwnPropertyDescriptor(obj, key);
   }
   return result;
}

应用场景上,主要用来完成精细的对象拷贝工作:

代码语言:javascript
复制
// 连带属性描述符原样搬过去
function clone(obj) {
 return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
}
// 会丢失不可枚举属性以及原描述符
function copy(obj) {
 return Object.assign({}, obj);
}

区别如下:

代码语言:javascript
复制
const obj = {};
Object.defineProperty(obj, 'bar', {
 value: 42,
 enumerable: false
});
Object.defineProperty(obj, 'foo', {
 value: 24,
 enumerable: true,
 writable: false
});Object.getOwnPropertyDescriptors(clone(obj));
// 属性保持原状
// bar: {value: 42, writable: false, enumerable: false, configurable: false}
// foo: {value: 24, writable: false, enumerable: true, configurable: false}Object.getOwnPropertyDescriptors(copy(obj));
// 不可枚举的bar丢了,foo的属性描述符被重置回默认了
// foo: {value: 24, writable: true, enumerable: true, configurable: true}

String padding

曾经引发npm风波的left-pad模块,以后用不着了:

代码语言:javascript
复制
str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])

有一些小细节,例如:

代码语言:javascript
复制
// 默认补空格(U+0020)
'1'.padStart(4) === '1'.padStart(4, ' ')
//  也可以填充指定串
'1'.padEnd(4, 0) === '1000'
// 填充串的长度不限于一个字符,太长会被裁剪掉
'1'.padEnd(4, 'abcde') === '1abc'
// 不用补就不补
'1345'.padStart(2) === '1345'

Trailing commas in function parameter lists and calls

基本语法的2个小变动:

代码语言:javascript
复制
function foo(
 param1,
 param2, // 形参列表允许有多余逗号
) {
 foo(
   'abc',
   'def',  // 实参列表允许有多余逗号
 );
}

实际上,类似的变动在ES5.1也发生过:

代码语言:javascript
复制
const object = {
 foo: "bar",
 baz: "qwerty",
 age: 42,  // 对象字面量键值对儿列表允许有多余逗号
};

除了上面3种,还有语言最初的语法规则:

代码语言:javascript
复制
const arr = [
 1,
 2,
 3,  // 数组字面量允许有多余逗号
];arr; // [1, 2, 3]
arr.length; // 3

特殊的:

代码语言:javascript
复制
const arr = [1, 2, 3,,,];
arr.length; // 5

在字面量形式的稀疏数组中,最后一个逗号属于trailing commas(末尾多余逗号)被忽略掉,因此数组大小是5

P.S.关于trailing commas的更多信息,见Trailing commas

五.总结

Async functions终于在ES2017加入豪华午餐了,多线程方面的基础建设也在逐步完善

此外,还有三个无关紧要的Object方法,一个字符串padding方法,参数列表末尾允许有多余逗号。对于这些锦上添花的东西,看到一个端正的态度

Are people seriously considering to extend the language for something that can be implemented in 7 lines of code ? Convenience matters, as does eliminating redundant code. JavaScript’s runtime library is still very spartan compared to other programming languages.

参考资料

  • ECMAScript® 2017 Language Specification (ECMA-262, 8th edition, June 2017)
  • ECMAScript 2017 (ES8): the final feature set
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端向后 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一.特性概览
  • 二.Async functions
  • 三.Shared memory and atomics
  • 四.小特性
    • Object.values/Object.entries
      • 枚举性,原型属性与Symbol
    • Object.getOwnPropertyDescriptors
      • String padding
        • Trailing commas in function parameter lists and calls
        • 五.总结
          • 参考资料
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档