ES7和ES8的新特性总结

根据 https://github.com/tc39/proposals/blob/master/finished-proposals.md,最后一次更新在8月4号,已经达到 stage 4 完成的提案有下面这些:

  1. Array.prototype.includes
  2. Exponentiation Operator
  3. Object.values / Object.entries
  4. String padding
  5. Object.getOwnPropertyDescriptors
  6. Trailing commas in function parameter lists and calls
  7. Async functions
  8. Shared memory and atomics
  9. Lifting template literal restriction

Array.prototype.includes

之前我们经常使用 indexOf 来检查数组中是否存在某个值,但尴尬的是它返回来的是 -1 或者值的位置,并不是一个布尔值,因此我们经常在代码上需要转换一下,这在逻辑上是并不明确的,比如如果不存在某个值,返回的 -1 其实是一个真值(truthy),而如果值的位置在首位,那么返回的 0 又是一个假值(falsey),导致我们只能写成:

if (arr.indexOf('xxx') !== -1) {
  // some code
}

作为 indexOf 方法的替代,以后我们就可以这么写:

if (arr.includes('xxx')) {
  // some code
}

使得代码语义上的可读性更强了。另外这个方法也可以用在 string 上。对于 NaN,这个方法一样可以识别:

[1, 2, NaN].includes(NaN)
// true

最后这个方法有一个可选的 fromIndex 第二参数,来判断是否在该位置:

// 数组内位置 1 是否是 'a'
['a', 'b', 'c'].includes('a', 1)
// false

Exponentiation Operator

指数运算在一些 3D,VR,SVG,数据可视化等经常需要使用到,之前我们只能使用Math.pow或者自行创建一些函数或循环来实现,在ES7中我们可以像python等语言一样,使用 ** 号来表示指数了。

7 ** 12 === Math.pow(7, 12)  // true
2 ** 7 === Math.pow(2, 7)  // true

同样的,也可以使用运算赋值符号:

let a = 8
a **= 12

a === Math.pow(8, 12)
// true

Object.values / Object.entries

Object.keys提供了对象本身的属性键值对合集,而 Object.values 则返回了所有值的合集。

let obj = {a: 1, b: 2, c: 3}

Object.values(obj)  // [1, 2, 3]

Object.entries 则将每一个键值对放入同一个数组内返回。

Object.entries(obj)  // [["a", 1], ["b", 2], ["c", 3]]

注意这两者的顺序和 Object.keys 是一样的,按照键名 - 数字按从小到大 - 字符串则按创建顺序 - symbol也按照创建顺序。

我们可以配合解构赋值来使用这两个方法:

let obj = {a: 1, b: 2, c: 3}
Object.entries(obj).forEach(([key, value]) => {
 console.log(`${key} is ${value}`)
})
// a is 1, b is 2, c is 3

当然 forEach 也可以用 ES6 的 for of 循环来替代:

let obj = {a: 1, b: 2, c: 3}
for (let [key, value] of Object.entries(obj)) {
  console.log(`${key} is ${value}`)
}
// a is 1, b is 2, c is 3

String padding

我们在css中经常会使用到padding来控制内边距的距离,在js中也可以类似这种概念来理解。

ES2017 引入字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。

使用 String.prototype.padStartString.prototype.padEnd 来分别在字符串的开头和末尾插入内容并返回固定长度的字符串。

'react'.padStart(10)
// return "     react"
// 注意前面有5个空格

'react'.padStart(10).length
// 10

在一些牵涉到数字计算的时候我们可以用其返回固定长度的字符串来方便对齐:

console.log('0.00'.padStart(20))
console.log('10,000.00'.padStart(20))
console.log('250,000.00'.padStart(20))

结果将统一长度:

                0.00
           10,000.00
          250,000.00

第二个可选参数用来说明需要插入的内容:

console.log('react'.padStart(10, '_'))
// "_____react"

console.log('backbone'.padStart(10, '*'))
// "**backbone"

第二参数的长度可以随意,但总会按照第一个参数输入的长度来截断:

console.log('react'.padEnd(10, ':-)'))
// "react:-):-" is 10

console.log('backbone'.padEnd(10, '*'))
// "backbone**" is 10

Object.getOwnPropertyDescriptors

看名字就知道了,返回所有对象自身属性的属性描述符。相比于 Object.getOwnPropertyDescriptor(obj, propName), 这个方法将所有的自身属性的描述符均放入数组内返回了。

这个方法的引入目的,主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。

我们在使用 ES6 的 Object.assign 来浅复制一个对象的时候,它总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。

这时,Object.getOwnPropertyDescriptors 方法配合Object.defineProperties 方法,就可以实现正确拷贝。

const shallowMerge = (target, source) => Object.defineProperties(
  target,
  Object.getOwnPropertyDescriptors(source)
);

另一个用法,是配合Object.create方法,将对象属性克隆到一个新对象。这也属于浅拷贝。

const shallowClone = obj => Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
)

更多的用法可以参考 http://es6.ruanyifeng.com/?search=assign&x=0&y=0#docs/object#Object-assign

Trailing commas in function parameter lists and calls

我们在使用数组和对象的时候,最后一个值的末尾都可以加逗号,而在函数的参数中是不行的。

const a = [1, 2, 3,]
// ok!

const b = {name: 'xxx', age: 18,}
// ok!

const c = function(a, b, c,) {
  // some code
}
// Uncaught SyntaxError: Unexpected token (

而这一特性引入后,这种写法也不会再报错了。

Async functions

async/await 是什么?一句话,它就是 Generator 函数的语法糖。限于篇幅,这篇文章就不细说了,之后再写一篇文章详细记录。

具体的内容可以参见http://es6.ruanyifeng.com/?search=assign&x=0&y=0#docs/async

Shared memory and atomics

共享内存允许多个线程并发读写数据,而原子操作则能够进行并发控制,确保多个存在竞争关系的线程顺序执行。关于这一点因为自己目前了解的还不多,这里就就不详细说明了,更多的内容可以参见这个简介的文档详细的规范

Lifting template literal restriction

这一特性因为要到2018年才发布,因此准确来说应该算是 ES2018(ES9) 的特性。

模版字符串中关于转义字符遵循下面的规则:

  • Unicode escapes started by "\u", for example \u00A9
  • Unicode code point escapes indicated by "\u{}", for example \u{2F804}
  • Hexadecimal escapes started by "\x", for example \xA9
  • Octal literal escapes started by "" and (a) digit(s), for example \251

因此有的时候带标签的模版字符串使用时会造成错误,比如:

注: 带标签的模版字符串其实是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。

latex`\unicode`
// Throws in older ECMAScript versions (ES2016 and earlier)
// SyntaxError: Invalid Unicode escape sequence

ES9 的这一特性则移除了这一限制。

ref