es2018新特性及实现原理

es2018(ES9)新特性

对象的扩展运算符

1
2
3
4
5
6
7
8
9
const obj1 = {a: 10};
const obj2 = {b: 20};
const obj3 = {c: 30};

// ES2018
console.log({...obj1, ...obj2, ...obj3}); // → {a: 10, b: 20, c: 30}

// ES2015
console.log(Object.assign({}, obj1, obj2, obj3)); // → {a: 10, b: 20, c: 30}

在进行对象融合时,Spread操作结果并不总是与Object.assign()一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Object.defineProperty(Object.prototype, 'a', {
set(value) {
console.log('set called!');
}
});

const obj = {a: 10};

console.log({...obj});
// → {a: 10}

console.log(Object.assign({}, obj));
// → set called!
// → {}

Object.assign()方法继承了setter属性;而spread操作忽略了setter

注意

  • 可枚举
  • 非继承
  • 浅拷贝

异步迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// es8异步迭代器
const justjavac = {
[Symbol.asyncIterator]: () => {
const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`];
return {
next: () => Promise.resolve({
done: items.length === 0,
value: items.shift()
})
}
}
}

for await (const item of justjavac) {
console.log(item)
}

看一下异步迭代器的实现

1
2
3
4
5
6
7
8
9
10
11
12
// 异步迭代器
// 普通迭代器直接返回 IteratorResult
interface AsyncIterator {
next(value) : Promise<IteratorResult>;
[optional] throw(value) : Promise<IteratorResult>;
[optional] return(value) : Promise<IteratorResult>;
}

interface IteratorResult {
value : any;
done : bool;
}

异步生成器函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 跑一下看结果
function convertToBase64Data(url) { // 转成base64格式
return new Promise( resolve => {
setTimeout( () => {
resolve(url);
}, 1000);
});
}
function saveToLocal(img) { // 保存到本地
return new Promise( resolve=> {
setTimeout( () => {
resolve("success");
}, 200);
});
}

async function* saveImages(img) {
let file = await saveToLocal("????")
console.log(file)
try {
for(let i = 0; i < 4; i++) {
let a = yield await convertToBase64Data(">>>")
console.log("hhhhh")
}
} finally {
let b = await saveToLocal("gun")
console.log(b)
}
}
// 注意 for-await-of 必须放在async里面
(async function(){
for await (const item of saveImages()) {
console.log(item)
}
})();

// 如下会执行到yield那里的await
saveImages().next().then()

Promise.prototype.finally

1
2
3
4
5
6
7
8
9
10
11
// 当您需要在操作完成后进行一些清理时,finally()方法就派上用场了
fetch('https://www.google.com')
.then((response) => {
console.log(response.status);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
document.querySelector('#spinner').style.display = 'none';
});

正则新特性

  • s (dotAll) 标志

    匹配除换行符(如换行符(\n)或回车符(\r)之外的任何字符

1
2
3
console.log(/one[\d\D]two/.test('one\ntwo'));    // → true
// s 激活 "." 模式
console.log(/one[\d\D]two/.test('one\ntwo')); // → true
  • 命名捕获组

    引入了使用(?…)语法的命名捕获组

1
2
3
4
5
6
7
8
// 
const re = /(?<year>)\d{4}/-(?<Month>\d{2})-(?<day>\d{2})/;
const match = re.exec("2019-01-10");

console.log(match.groups); // → {year: "2019", month: "01", day: "10"}
console.log(match.groups.year); // → 2019
console.log(match.groups.month); // → 01
console.log(match.groups.day); // → 10

使用\k语法重复调用名称捕获组

1
2
3
4
5
6
7
// 在一个在句子中找到连续重复的单词 
// \b为词边界
const re = /\b(?<dup>\w+)\s+\k<dup>\b/;
const match = re.exec('Get that that cat off the table!');

console.log(match.index); // → 4
console.log(match[0]); // → that that

注意

要将命名捕获组插入replace()方法的替换字符串中,需要使用$构造。

1
2
3
const str = 'red & blue';
console.log(str.replace(/(?<red>red) & (?<blue>blue)/, '$<blue> & $<red>'));
// → blue & red
-------------要说再见啦感谢大佬的光临~-------------