ES2017
一.特性概览
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(原子操作)用来解决数据同步的问题,如加锁、事务
例如:
// 主线程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:
var sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 100000); // 100000 primesvar ia = new Int32Array(sab); // ia.length == 100000var primes = new PrimeGenerator();for ( let i=0 ; i < ia.length ; i++ ) ia[i] = primes.next();w.postMessage(ia);
由于数据是多线程共享的,势必面临数据同步的问题,通过Atomics全局对象提供的一些方法来解决:
// 读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),在此基础上可以实现:
保证连续读写操作的顺序
避免写操作“丢失”(比如写到脏数据上了)
此外,还允许挂起/唤醒(更友好的线程等待方式,不多占资源):
Atomics.wait(typedArray, index, value[, timeout])Atomics.wake(typedArray, index, count)
例如:
// A线程写console.log(ia[37]); // Prints 163Atomics.store(ia, 37, 123456);Atomics.wake(ia, 37, 1);// B线程等着读Atomics.wait(ia, 37, 163);console.log(ia[37]); // Prints 123456而不需要靠死循环来实现阻塞式等待:while (Atomics.load(ia, 37) == 163);console.log(ia[37]); // Prints 123456P.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
// 返回 (1)自身的 (2)可枚举的 (3)非Symbol类型的 属性的值
Object.values(obj)
polyfill实现大致如下:
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:
function values(obj) { return Object.keys(obj).map(key => obj[key]);}
类似的,还提供了:
// 返回 (1)自身的 (2)可枚举的 (3)非Symbol类型的 属性的键值对儿
Object.entries(obj)
polyfill也类似:
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的工作:
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-onlyObject.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
// 以对象字典形式返回 (1)自身的 所有属性的描述符
Object.getOwnPropertyDescriptors(obj)
包括Symbol类型属性与不可枚举属性,例如:
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:
function getOwnPropertyDescriptors(obj) { const result = {}; for (let key of Reflect.ownKeys(obj)) { result[key] = Object.getOwnPropertyDescriptor(obj, key); } return result;}
应用场景上,主要用来完成精细的对象拷贝工作:
// 连带属性描述符原样搬过去function clone(obj) { return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));}// 会丢失不可枚举属性以及原描述符function copy(obj) { return Object.assign({}, obj);}
区别如下:
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模块,以后用不着了:
str.padStart(targetLength [, padString])str.padEnd(targetLength [, padString])
有一些小细节,例如:
// 默认补空格(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个小变动:
function foo( param1, param2, // 形参列表允许有多余逗号) { foo( 'abc', 'def', // 实参列表允许有多余逗号 );}
实际上,类似的变动在ES5.1也发生过:
const object = { foo: "bar", baz: "qwerty", age: 42, // 对象字面量键值对儿列表允许有多余逗号};
除了上面3种,还有语言最初的语法规则:
const arr = [ 1, 2, 3, // 数组字面量允许有多余逗号];arr; // [1, 2, 3]arr.length; // 3
特殊的:
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
更多相关文章
- jQuery——将title属性用作悬停的文本,但只在同一父类中使用。
- jQuery编程基础精华02(属性、表单过滤器,元素的each,表单选择器,子元
- jQuery返回一个没有逗号的字符串的前5个单词
- Google地图信息窗口左箭头,右箭头传递TypeError:无法读取未定义的
- 3.29 学前端 jquery之操作元素之属性操作
- 获取复选框的文本属性
- 将JSON结果返回给ajax请求的MVC ErrorHandling属性仅适用于local
- 将JavaScript数组转换成逗号分隔列表的简单方法?
- JQuery属性与样式――.val()和增加样式.addClass()