这篇文章上次修改于 250 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

ES7~ES8的新特性

[TOC]

ES7(ES2016)

在ES6基础上新增了两项功能:数组的includes方法和Math.pow的简单写法

Array.prototype.includes()

之前我们用indexOf一个数组或字符串是否存在某个数据

const arr = [1, 2, '12'];

console.log(arr.indexOf('23') !== -1);  // ==> false
console.log(~arr.indexOf('23') !== 0);  // ==> false

现在可以

log(arr.incldues('23'));  // ==> false

//对NaN的判断
[1, 2, NaN].includes(NaN)     // => true
[1, 2, NaN].indexOf(NaN)  // ==> -1

指数操作符(**)与Math.pow()

console.log(Math.pow(2, 3)); // ==> 8
console.log(2 ** 3); // ==> 8

ES8(ES2017)

async/await

在ES6中为了解决回调的书写方式,引入了Promise的then函数,业务逻辑很多的时候,需要链式多个then函数,语义会变得很不清楚。

new Promise((resolve, reject) => {this.login(resolve)})
.then(() => this.getInfo())
.then(() => {// do something})
.catch(() => { console.log("Error") })

Generator函数应运而生,它只是一个分段执行任务,通过状态指针next分布执行任务,但是流程管理不太方便(每个阶段何时执行不太明了),所以它只是一个中间产物。

const gen = function* () {
  const f1 = yield this.login()
  const f2 = yield this.getInfo()
};

async函数是Generator函数的语法糖,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await。但async函数不像Generator函数必须需要执行器(co模块)才能执行,async就是一个普通函数,它的返回值是一个Promise对象,await是then(then可以是其他对象里的实例)的语法糖,await会等待当前异步操作完成才能执行下面的代码语句,所以应用起来感觉更像是同步操作。

async function func() {
    return await '123';  // '123'  会被隐式转化为 Promise.resolve('123') 的Promise对象
}
func().then(data => {
    console.log(data); // ==> '123'
});

Object.values()

返回对象所有属性的值,类型为数组

var obj = {a: 1, b: 'xiaoliu', c: {a: 1}};

// 之前
const vals = Object.keys(obj).map(key => {
  return obj[key];
})

// 现在
const new_vals = Object.values(obj);

console.log(vals, new_vals); // Array [ 1, 'xiaoliu', { a: 1 } ]

Object.entries()

返回对象可枚举的键值对数组,eg:Array [ [ 'a', 1 ], [ 'b', 'xiaoliu' ], [ 'c', { a: 1 } ] ]

之前可用for...inObject.keys(obj)来遍历对象的键,进而由键找出对应的建值

var obj = Object.create({a: 1, b: 'xiaoliu', c: {a: 1}});
var _obj = Object.create(obj);
obj.a = 2;
_obj.a = 6;
_obj.b = 6;

for(let key in _obj) {
  console.log(key, _obj[key]);
}
/*
  a 6
  b 6
  c { a: 1 }
*/

由上面的例子不难看出,for...in方法还遍历了原型链上的属性,并且当属性名重复时会被覆盖掉。

Object.entries只会遍历当前对象上可被权举的属性和属性值,不会原型链上的属性

var obj = Object.create({a: 1, b: 'xiaoliu', c: {a: 1}});
var _obj = Object.create(obj);
obj.a = 2;
_obj.a = 6;
_obj.b = 6;

Object.entries(_obj).forEach(([key, value]) => {
  console.log(key, value);
});
/*
  a 6
  b 6
*/

for(let [key, value] of Object.entries(_obj)) {
  console.log(key, value);
}
/*
  a 6
  b 6
*/

Object.entries还能作为Map函数的参数并生成Map对象

console.log(new Map(Object.entries(obj)));
// ==> Map { 'a' => 1, 'b' => 'xiaoliu', 'c' => { a: 1 } }

String Padding

ES8为String对象添加了两个方法String.prototype.padStart()String.prototype.padEnd()

String.prototype.padStart(targetLength[, padString])

用一个字符串从前面填充当前字符串,不改变原字符串值

  • targetLength 填充的目标字符串长度,小于当前字符串长度返回当前字符串
  • padString 可选参数,要填充的字符串
const STR = 'abc';
console.log(
  STR.padStart(10, '123'),// ==> '1231231abc'
  STR.padStart(7),  // ==> '    abc'
  STR.padStart(2, '123'), // ==> 'abc'
  STR.padStart(0, '123'), // ==> 'abc'
  STR.padStart(0) // ==> 'abc'
);

String.prototype.padEnd(targetLength[, padString])

用一个字符串从后面填充当前字符串, 不改变原字符串值

  • targetLength 填充的目标字符串长度,小于当前字符串长度返回当前字符串
  • padString 可选参数,要填充的字符串
const STR = 'abc';
console.log(
  STR.padEnd(10, '123'),// ==> 'abc1231231'
  STR.padEnd(7),  // ==> 'abc    '
  STR.padEnd(2, '123'), // ==> 'abc'
  STR.padEnd(0, '123'), // ==> 'abc'
  STR.padEnd(0) // ==> 'abc'
);

结尾允许逗号,如函数形参、对象、数组等

function func(a, b,) {
  console.log(a, b);
}
func(1, 2,); // ==> 1 2

const obj = {a: 1,};
console.log(obj); // ==> { a: 1 }

Object.prototype.getOwnPropertyDescriptors()

获取对象每个属性的属性描述符

const obj = {a: 1, b: '23'};
console.log(Object.getOwnPropertyDescriptors(obj));

/*
{ 
  a: {
    vcalue: 1,
    writable: true,
    enumerable: true,
    configurable: true
  },
  b: {
    value: '23',
    writable: true,
    enumerable: true,
    configurable: true
  }
}
*/

SharedArrayBuffer、Atomics

SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,它们都可以用来在共享内存(shared memory)上创建视图。与 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分离。

Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。这些原子操作属于 Atomics 模块。与一般的全局对象不同,Atomics 不是构造函数,因此不能使用 new 操作符调用,也不能将其当作函数直接调用。Atomics 的所有属性和方法都是静态的(与 Math 对象一样)。

所以我们可以用SharedArrayBuffer来结合Web Worker来进行线程的数据内存共享,Atomics避免了线程竞争对数据的胡乱篡改,相当于加锁,通俗讲就是对这块内存数据的读、改,一次只允许一个线程对其进行操作。

看个简单的例子

//  index.js
// 申请一个工作线程
const worker = new Worker('./work.js');

// 创建1kb的内存空间
const sharedBuffer = new SharedArrayBuffer(1024);

//建视图
const intArrBuffer = new Int32Array(sharedBuffer);
for(let i = 0; i < intArrBuffer.length; i ++) {
  intArrBuffer[i] = [i];
}
// 向工作线程共享这块内存
worker.postMessage(intArrBuffer);

// 监控工作线程回返的信息
worker.onmessage = function (e) {
  console.log(intArrBuffer[30]);
}
// work.js
onmessage = function(e) {
  let arrBuffer = e.data; // 获取内存数据
  console.log(Atomics.load(arrBuffer, 30));
  Atomics.store(arrBuffer, 30, 99);// 修改内存数据
  postMessage('work');
  self.close();
}

创建一个html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
</head>
<body>
  <script src="./index.js"></script>
</body>
</html>

然后启动一个服务,才能进行后续操作,使用npm全局包http-server

浏览器访问得到结果

//  29
//  99

Atomics的常用方法

Atomics.add()

将指定位置上的数组元素与给定的值相加,并返回相加前该元素的值。

Atomics.and()

将指定位置上的数组元素与给定的值相与,并返回与操作前该元素的值。

Atomics.compareExchange()

如果数组中指定的元素与给定的值相等,则将其更新为新的值,并返回该元素原先的值。

Atomics.exchange()

将数组中指定的元素更新为给定的值,并返回该元素更新前的值。

Atomics.load()

返回数组中指定元素的值。

Atomics.or()

将指定位置上的数组元素与给定的值相或,并返回或操作前该元素的值。

Atomics.store()

将数组中指定的元素设置为给定的值,并返回该值

Atomics.wait()

检测数组中某个指定位置上的值是否仍然是给定值,是则保持挂起直到被唤醒或超时。返回值为 "ok"、"not-equal" 或 "time-out"。调用时,如果当前线程不允许阻塞,则会抛出异常(大多数浏览器都不允许在主线程中调用 wait())。

Atomics.wake()

唤醒等待队列中正在数组指定位置的元素上等待的线程。返回值为成功唤醒的线程数量。

Atomics.isLockFree(size)

可以用来检测当前系统是否支持硬件级的原子操作。对于指定大小的数组,如果当前系统支持硬件级的原子操作,则返回 true;否则就意味着对于该数组,Atomics 对象中的各原子操作都只能用锁来实现。此函数面向的是技术专家。

需要注意的是除了Chrome和Firefox浏览器,其他主流浏览器均不支持