# ES 新特性
ECMAScript
ECMAScript 是一种由 Ecma 国际(前身为欧洲计算机制造商协会)在标准 ECMA-262 中定义的脚本语言规范。这种语言在万维网上应用广泛,它往往被称为 JavaScript 或 JScript,但实际上后两者是 ECMA-262 标准的实现和扩展。
至发 2020.01.03 为止有九个 ECMA-262 版本发表。其历史版本如下:
- 1997 年 6 月:第一版
- 1998 年 6 月:修改格式,使其与 ISO/IEC16262 国际标准一样
- 1999 年 12 月:强大的正则表达式,更好的词法作用域链处理,新的控制指令,异常处理,错误定义更加明确,数据输出的格式化及其它改变
- 2009 年 12 月:添加严格模式("use strict")。修改了前面版本模糊不清的概念。增加了 getters,setters,JSON 以及在对象属性上更完整的反射。
- 2011 年 6 月:ECMAScript 标 5.1 版形式上完全一致于国际标准 ISO/IEC 16262:2011。
- 2015 年 6 月:ECMAScript 2015(es2015),第 6 版,最早被称作是 ECMAScript 6(ES6),添加了类和模块的语法,其他特性包括迭代器,Python 风格的生成器和生成器表达式,
箭头函数,二进制数据,静态类型数组,集合(maps,sets 和 weak maps),promise,reflection 和 proxies。作为最早的 ECMAScript Harmony 版本,也被叫做 ES6 Harmony。 - 2016 年 6 月:ECMAScript 2016(es2016),第 7 版,多个新的概念和语言特性。
- 2017 年 6 月:ECMAScript 2017(es2017),第 8 版,多个新的概念和语言特性。
- 2018 年 6 月:ECMAScript 2018 (es2018),第 9 版,包含了
异步循环,生成器,新的正则表达式特性和 rest/spread 语法。 - 2019 年 6 月:ECMAScript 2019 (es2019),第 10 版。
- 2020 年 6 月:ECMAScript 2020 (es2020),第 11 版。
- 2021 年 6 月:ECMAScript 2021 (es2021),第 12 版。
- 2022 年 6 月 22 日,第 123 届 ECMA 大会批准了 ECMAScript 2022 语言规范
# es2022
- Top-level Await
- Object.hasOwn()
- at()
- error.cause
- 正则表达式匹配索引
- 类的实例成员
# Top-level Await
在 ES2017 中,引入了 async 函数和 await 关键字,以简化 Promise 的使用,但是 await 关键字只能在 async 函数内部使用。尝试在异步函数之外使用 await 就会报错:SyntaxError - SyntaxError: await is only valid in async function。
TIP
顶层 await 允许我们在 async 函数外面使用 await 关键字。它允许模块充当大型异步函数,通过顶层 await,这些 ECMAScript 模块可以等待资源加载。这样其他导入这些模块的模块在执行代码之前要等待资源加载完再去执行。
// a.js
const resp = await fetch("https://jsonplaceholder.typicode.com/users");
const users = resp.json();
export { users };
// usingAwait.js
import { users } from "./a.js";
console.log(users);
console.log("usingAwait module");
2
3
4
5
6
7
8
9
10
# 我们还可以立即调用顶层 async 函数(IIAFE)
import fetch from "node-fetch";
(async () => {
const resp = await fetch("https://jsonplaceholder.typicode.com/users");
users = resp.json();
})();
export { users };
2
3
4
5
6
WARNING
这样会有一个缺点,直接导入的 users 是 undefined,需要在异步执行完成之后才能访问它:
// usingAwait.js
import { users } from "./a.js";
console.log("users:", users); // undefined
setTimeout(() => {
console.log("users:", users);
}, 100);
console.log("usingAwait module");
2
3
4
5
6
7
当然,这种方法并不安全,因为如果异步函数执行花费的时间超过 100 毫秒, 它就不会起作用了,users 仍然是 undefined。
另一个方法是导出一个 promise,让导入模块知道数据已经准备好了:
//a.js
import fetch from "node-fetch";
export default (async () => {
const resp = await fetch("https://jsonplaceholder.typicode.com/users");
users = resp.json();
})();
export { users };
//usingAwait.js
import promise, { users } from "./a.js";
promise.then(() => {
console.log("usingAwait module");
setTimeout(() => console.log("users:", users), 100);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
虽然这种方法似乎是给出了预期的结果,但是有一定的局限性:导入模块必须了解这种模式才能正确使用它。
而顶层 await 就可以解决这些问题:
// a.js
const resp = await fetch("https://jsonplaceholder.typicode.com/users");
const users = resp.json();
export { users };
// usingAwait.js
import { users } from "./a.mjs";
console.log(users);
console.log("usingAwait module");
2
3
4
5
6
7
8
9
顶级 await 在以下场景中将非常有用:
// 动态加载模块:
const strings = await import(`/i18n/${navigator.language}`);
// 资源初始化:
const connection = await dbConnector();
// 依赖回退:
let translations;
try {
translations = await import("https://app.fr.json");
} catch {
translations = await import("https://fallback.en.json");
}
2
3
4
5
6
7
8
9
10
11
# Object.hasOwn()
在 ES2022 之前,可以使用
Object.prototype.hasOwnProperty()来检查一个属性是否属于对象。
Object.hasOwn特性是一种更简洁、更可靠的检查属性是否直接设置在对象上的方法:
const example = {
property: "123",
};
console.log(Object.prototype.hasOwnProperty.call(example, "property"));
console.log(Object.hasOwn(example, "property"));
2
3
4
5
6
# at()
at() 是一个数组方法,用于通过给定索引来获取数组元素。当给定索引为正时,这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时,就会从数组的最后一项开始检索:
除了数组,字符串也可以使用 at()方法进行索引
const array = [0, 1, 2, 3, 4, 5];
console.log(array[array.length - 1]); // 5
console.log(array.at(-1)); // 5
console.log(array[array.lenght - 2]); // 4
console.log(array.at(-2)); // 4
const str = "hello world";
console.log(str[str.length - 1]); // d
console.log(str.at(-1)); // d
2
3
4
5
6
7
8
9
10
11
12
# error.cause
在 ECMAScript 2022 规范中,new Error() 中可以指定导致它的原因:
function readFiles(filePaths) {
return filePaths.map((filePath) => {
try {
// ···
} catch (error) {
throw new Error(`While processing ${filePath}`, { cause: error });
}
});
}
2
3
4
5
6
7
8
9
# 正则表达式匹配索引
该特性允许我们利用 d 字符来表示我们想要匹配字符串的开始和结束索引。以前,只能在字符串匹配操作期间获得一个包含提取的字符串和索引信息的数组。在某些情况下,这是不够的。因此,在这个规范中,如果设置标志 /d,将额外获得一个带有开始和结束索引的数组。
const matchObj = /(a+)(b+)/d.exec("aaaabb");
console.log(matchObj[1]); // 'aaaa'
console.log(matchObj[2]); // 'bb'
2
3
4
由于 /d 标识的存在,matchObj 还有一个属性.indices,它用来记录捕获的每个编号组:
console.log(matchObj.indices[1]); // [0, 4]
console.log(matchObj.indices[2]); // [4, 6]
2
我们还可以使用命名组:
const matchObj = /(?<as>a+)(?<bs>b+)/d.exec("aaaabb");
console.log(matchObj.groups.as); // 'aaaa'
console.log(matchObj.groups.bs); // 'bb'
2
3
这里给两个字符匹配分别命名为as和bs,然后就可以通过 groups 来获取到这两个命名分别匹配到的字符串。
它们的索引存储在 matchObj.indices.groups 中:
console.log(matchObj.indices.groups.as); // [0, 4]
console.log(matchObj.indices.groups.bs); // [4, 6]
2
匹配索引的一个重要用途就是指向语法错误所在位置的解析器。下面的代码解决了一个相关问题:它指向引用内容的开始和结束位置。
const reQuoted = /“([^”]+)”/dgu;
function pointToQuotedText(str) {
const startIndices = new Set();
const endIndices = new Set();
for (const match of str.matchAll(reQuoted)) {
const [start, end] = match.indices[1];
startIndices.add(start);
endIndices.add(end);
}
let result = "";
for (let index = 0; index < str.length; index++) {
if (startIndices.has(index)) {
result += "[";
} else if (endIndices.has(index + 1)) {
result += "]";
} else {
result += " ";
}
}
return result;
}
console.log(pointToQuotedText("They said “hello” and “goodbye”.")); // ' [ ] [ ] '
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 类的实例成员
# 公共实例字段
公共类字段允许我们使用赋值运算符 (=) 将实例属性添加到类定义中。下面是一个计数器的例子:
import React, { Component } from "react";
export class Incrementor extends Component {
constructor() {
super();
this.state = { count: 0 };
this.increment = this.increment.bind(this);
}
increment() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<button onClick={this.increment}>Increment: {this.state.count}</button>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在这个例子中,在构造函数中定义了实例字段和绑定方法,通过新的类语法,可以使代码更加直观。新的公共类字段语法允许我们直接将实例属性作为属性添加到类上,而无需使用构造函数方法。这样就简化了类的定义,使代码更加简洁、可读:
import React from "react";
export class Incrementor extends React.Component {
state = { count: 0 };
increment = () => this.setState({ count: this.state.count + 1 });
render = () => (
<button onClick={this.increment}>Increment: {this.state.count}</button>
);
}
2
3
4
5
6
7
8
# es2021
# replaceAll
返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉
const str = "hello world";
str.replaceAll("l", ""); // "heo word"
2
# Promise.any
Promise.any() 接收一个 Promise 可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise
const promise1 = new Promise((resolve, reject) =>
reject("我是失败的Promise_1")
);
const promise2 = new Promise((resolve, reject) =>
reject("我是失败的Promise_2")
);
const promiseList = [promise1, promise2];
Promise.any(promiseList)
.then((values) => {
console.log(values);
})
.catch((e) => {
console.log(e);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
# 逻辑运算符和赋值表达式
逻辑运算符和赋值表达式,新特性结合了逻辑运算符(
&&,||,??)和赋值表达式而 JavaScript 已存在的 复合赋值运算符有:
a ||= b;
//等价于
a = a || (a = b);
let a = ""; // null undefined 0
a ||= "8"; // a = '8'
a &&= b;
//等价于
a = a && (a = b);
let a = ""; // null undefined 0
a &&= "9"; // a = ''
a ??= b;
//等价于
a = a ?? (a = b);
let a = ""; // 0
a ??= "7"; // a = ''; a = 0
let b = null; // undefined
b ??= 9; // b = 9
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 5. 数字分隔符
数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性
const money = 1_000_000_000;
//等价于
const money = 1000000000;
1_000_000_000 === 1000000000; // true
2
3
4
5
# es2020
# Nullish coalescing Operator(空值处理)
表达式在
??的左侧 运算符求值为undefined或null,返回其右侧。【但是对于0或者''】则不会跳过
let user = {
u1: 0,
u2: false,
u3: null,
u4: undefined
u5: '',
}
let u1 = user.u1 ?? '用户1' // 0
let u2 = user.u2 ?? '用户2' // false
let u3 = user.u3 ?? '用户3' // 用户3
let u4 = user.u4 ?? '用户4' // 用户4
let u5 = user.u5 ?? '用户5' // ''
2
3
4
5
6
7
8
9
10
11
12
# Optional chaining(可选链)
?.用户检测不确定的中间节点
# Promise.allSettled
返回一个在所有给定的 promise 已被决议或被拒绝后决议的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) =>
reject("我是失败的Promise_1")
);
const promise4 = new Promise((resolve, reject) =>
reject("我是失败的Promise_2")
);
const promiseList = [promise1, promise2, promise3, promise4];
Promise.allSettled(promiseList).then((values) => {
console.log(values);
});
2
3
4
5
6
7
8
9
10
11
12
# import()
按需导入
# 新基本数据类型 BigInt
任意精度的整数
# globalThis
- 浏览器:window
- worker:self
- node:global
# es2019
# 更加友好的 JSON.stringify
JavaScript(JS)是最流行的编程语言之一,也是 Web 开发的主要语言之一,它在不断发展,每一次迭代都会带来一些新的内部变化。让我们来看看 es2019 的一些新功能,它们可能很快就会进入我们的日常代码:
# 行段分隔符
行段分隔符
行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与 JSON 匹配
以前,这些符号在字符串文字中被视为行终止符,因此使用它们会导致 SyntaxError 异常。
# 新增了 Array 的 flat()方法和 flatMap()方法
flat()和 flatMap()本质上就是是归纳(reduce) 与 合并(concat)的操作。
现在我们可以将嵌套数组按照指定的深度递归展开。默认值是 1,如果想完全展开则使用 Infinity。该方法不会修改原始数组,而是创建一个新的数组:
const arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1, 2, 3, 4]
const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(2); // [1, 2, 3, 4, 5, 6]
const arr3 = [1, 2, [3, 4, [5, 6, [7, 8]]]];
arr3.flat(2); //[1,2,3,4,[5,6,[7,8]]]
arr3.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8]
2
3
4
5
6
7
如果你的数组中有一个空槽,它将被删除:
const arr4 = [1, 2, , 4, 5];
arr4.flat(); // [1, 2, 4, 5]
2
# Array_flatMap
这是一个全新的方法,它结合了基本的 map 函数,然后使用新的 Array.flat()方法将结果展平 1 个深度:
const arr1 = [1, 2, 3];
arr1.map((x) => [x - 4]); // [[4], [8], [12]]
arr1.flatMap((x) => [x - 4]); // [4, 8, 12]
2
3
另一个更有用的例子:
const sentence = ["This is a", "regular", "sentence"];
sentence.map((x) => x.split(" ")); // [["This","is","a"],["regular"],["sentence"]]
sentence.flatMap((x) => x.split(" ")); // ["This","is","a","regular", "sentence"]
2
3
# 新增了 String 的 trimStart()方法和 trimEnd()方
除了同时删除字符串两边的空格的 String.Trim()之外,现在还出现了另一个方法只删除字符串两边任意一边的空格:
const test = " hello ";
test.trim(); // "hello";
test.trimStart(); // "hello ";
test.trimEnd(); // " hello";
2
3
4
# Object.fromEntries()
这是一个全新的方法:根据提供的键值对生成对象。它是我们熟悉的函数 Object.Entries 的逆向操作。 (Object.Entries 将对象转换为数组,以便更容易地进行操作。)在转换之后,我们会得到一个数组,但是现在我们可以将调整过后的数组返回到一个对象中。让我们试着用一个例子,我们想平方所有对象属性的值:
const obj = { prop1: 2, prop2: 10, prop3: 15 };
let array = Object.entries(obj); // [["prop1", 2], ["prop2", 10], ["prop3", 15]]
2
让我们用一个简单的映射将新生成的键值对的值平方:
array = array.map(([key, value]) => [key, Math.pow(value, 2)]); // [["prop1", 4], ["prop2", 100], ["prop3", 225]]
我们已经转换了对象值,但我们只剩下一个数组,这时候我们就需要 Object.fromEntries,它将数组转换回一个对象:
const newObj = Object.fromEntries(array); // {prop1: 4, prop2: 100, prop3: 225}
# 简化 try {} catch {},修改 catch 绑定
这个新提案使我们能够完全省略 catch()块,因为在很多情况下,我们并不需要它:
try {
//...
} catch (er) {
//handle error with parameter er
}
try {
//...
} catch {
//handle error without parameter
}
2
3
4
5
6
7
8
9
10
# 新的基本数据类型 BigInt
现在的基本数据类型(值类型)不止 5 种(ES6 之后是六种)了哦!加上 BigInt 一共有七种基本数据类型,分别是:
String、Number、Boolean、Null、Undefined、Symbol、BigInt
# Symbol.prototype.description
我们现在可以任意访问一个 Symbol 的 description 属性,无需借助 toString()方法。
const testSymbol = Symbol("Desc");
testSymbol.description; // "Desc"
2
# String.prototype.matchAll
matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器。 在 matchAll 出现之前,通过在循环中调用 regexp.exec 来获取所有匹配项信息(regexp需使用/g标志):
const regexp = RegExp("foo*", "g");
const str = "table football, foosball";
while ((matches = regexp.exec(str)) !== null) {
console.log(`Found ${matches[0]}. Next starts at ${regexp.lastIndex}.`);
// expected output: "Found foo. Next starts at 9."
// expected output: "Found foo. Next starts at 19."
}
2
3
4
5
6
7
如果使用 matchAll ,就可以不必使用 while 循环加 exec 方式(且正则表达式需使用/g 标志)。使用 matchAll 会得到一个
迭代器的返回值,配合 for...of, array spread, or Array.from()可以更方便实现功能:
const regexp = RegExp("foo*", "g");
const str = "table football, foosball";
let matches = str.matchAll(regexp);
for (const match of matches) {
console.log(match);
}
// Array [ "foo" ]
// Array [ "foo" ]
// matches iterator is exhausted after the for..of iteration
// Call matchAll again to create a new iterator
matches = str.matchAll(regexp);
Array.from(matches, (m) => m[0]);
// Array [ "foo", "foo" ]
2
3
4
5
6
7
8
9
10
11
12
13
matchAll 可以更好的用于分组
var regexp = /t(e)(st(\d?))/g;
var str = "test1test2";
str.match(regexp);
// Array ['test1', 'test2']
let array = [...str.matchAll(regexp)];
array[0];
// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
array[1];
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]
2
3
4
5
6
7
8
9
10
# Function.prototype.toString()现在返回精确字符,包括空格和注释
现在,我们对一个函数调用 toString(),将完全按照定义的方式返回函数,包括空格和注释。之前我们有:
function /- foo comment */ foo() {}
foo.toString(); // "function foo() {}"
2
然后现在是:
foo.toString(); // "function /- foo comment */ foo() {}"
// 箭头函数特殊
const bar /* comment */ = /* another comment */ () => {};
console.log(bar.toString()); // () => {}
2
3
4
# JSON_parse
现在,行分隔符 (u2028) 和段落分隔符 (u2029) 能被正确地解析,而不会导致 SyntaxError
# es2018
- 异步迭代
- Promise.finally()
- Rest/Spread 属性
- 正则表达式命名捕获组(Regular Expression Named Capture Groups)
- 正则表达式反向断言(lookbehind)
- 正则表达式 dotAll 模式
- 正则表达式 Unicode 转义
- 非转义序列的模板字符串
# 异步迭代
在
async/await的某些时刻,你可能尝试在同步循环中调用异步函数。例如:
async function process(array) {
for (let i of array) {
await doSomething(i);
}
}
//上面这段代码不会正常运行,下面这段同样也不会:
async function process(array) {
array.forEach(async (i) => {
await doSomething(i);
});
}
2
3
4
5
6
7
8
9
10
11
es2018 引入异步迭代器(
asynchronous iterators),这就像常规迭代器,除了 next()方法返回一个 Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。例如:
async function process(array) {
for await (let i of array) {
doSomething(i);
}
}
2
3
4
5
# pf
一个 Promise 调用链要么
成功到达最后一个.then(),要么失败触发.catch()。在某些情况下,你想要在无论 Promise 运行成功还是失败,运行相同的代码,例如清除,删除对话,关闭数据库连接等。
- .finally()允许你指定最终的逻辑
function doSomething() {
doSomething1()
.then(doSomething2)
.then(doSomething3)
.catch((err) => {
console.log(err);
})
.finally(() => {
// finish here!
});
}
2
3
4
5
6
7
8
9
10
11
# rs 属性
es2015 引入了
Rest参数和扩展运算符。三个点(...)仅用于数组。Rest 参数语法允许我们将一个不定数量的参数表示为一个数组。
function restParam(p1, p2, ...p3) {
// p1 = 1
// p2 = 2
// p3 = [3, 4, 5]
}
restParam(1, 2, 3, 4, 5);
2
3
4
5
6
7
展开操作符以相反的方式工作,将数组转换成可传递给函数的单独参数。例如 Math.max()返回给定数字中的最大值:
const values = [99, 100, -1, 48, 16];
console.log(Math.max(...values)); // 100
2
es2018 为对象解构提供了和
数组一样的Rest参数()和展开操作符,一个简单的例子:
const myObject = {
a: 1,
b: 2,
c: 3,
};
const { a, ...x } = myObject;
// a = 1
// x = { b: 2, c: 3 }
2
3
4
5
6
7
8
或者你可以使用它给函数传递参数:
restParam({
a: 1,
b: 2,
c: 3,
});
function restParam({ a, ...x }) {
// a = 1
// x = { b: 2, c: 3 }
}
2
3
4
5
6
7
8
9
- 跟数组一样,
Rest参数只能在声明的结尾处使用。此外,它只适用于每个对象的顶层,如果对象中嵌套对象则无法适用。
扩展运算符可以在其他对象内使用,例如:
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { ...obj1, z: 26 };
// obj2 is { a: 1, b: 2, c: 3, z: 26 }
2
3
可以使用扩展运算符拷贝一个对象,像是这样
obj2 = {...obj1},但是这只是一个对象的浅拷贝。另外,如果一个对象 A 的属性是对象 B,那么在克隆后的对象 cloneB 中,该属性指向对象 B。
# 命名捕获组
JavaScript 正则表达式
可以返回一个匹配的对象——一个包含匹配字符串的类数组,例如:以 YYYY-MM-DD 的格式解析日期:
const reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/,
match = reDate.exec("2018-04-30"),
year = match[1], // 2018
month = match[2], // 04
day = match[3]; // 30
2
3
4
5
es2018 允许命名捕获组使用符号
?<name>,在打开捕获括号(后立即命名,示例如下:
const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
match = reDate.exec("2018-04-30"),
year = match.groups.year, // 2018
month = match.groups.month, // 04
day = match.groups.day; // 30
2
3
4
5
- 任何匹配
失败的命名组都将返回undefined。
命名捕获也可以使用在replace()方法中。例如将日期转换为美国的 MM-DD-YYYY 格式:
const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
d = "2018-04-30",
usDate = d.replace(reDate, "$<month>-$<day>-$<year>"); //04-30-2018
2
3
# 反向断言
目前 JavaScript 在正则表达式中
支持先行断言(lookahead)。这意味着匹配会发生,但不会有任何捕获,并且断言没有包含在整个匹配字段中。例如从价格中捕获货币符号:
const reLookahead = /\D(?=\d+)/,
match = reLookahead.exec("$123.89");
console.log(match[0]); // $
2
3
- es2018 引入以相同方式工作但是匹配前面的
反向断言(lookbehind),这样我就可以忽略货币符号,单纯的捕获价格的数字:
const reLookbehind = /(?<=\D)\d+/,
match = reLookbehind.exec("$123.89");
console.log(match[0]); // 123.89
2
3
- 以上是 肯定反向断言,
非数字\D必须存在。同样的,还存在 否定反向断言,表示一个值必须不存在,例如:
const reLookbehindNeg = /(?<!\D)\d+/,
match = reLookbehind.exec("$123.89");
console.log(match[0]); // null
2
3
# dotAll
正则表达式中
点.匹配除回车外的任何单字符,标记s改变这种行为,允许行终止符的出现,例如:
/hello.world/.test("hello\nworld"); // false
/hello.world/s.test("hello\nworld"); // true
2
# unicode 转义
到目前为止,在
正则表达式中本地访问 Unicode 字符属性是不被允许的。es2018 添加了 Unicode 属性转义——形式为\p{...}和\P{...},在正则表达式中使用标记 u (unicode) 设置,在\p块儿内,可以以键值对的方式设置需要匹配的属性而非具体内容。例如:
const reGreekSymbol = /\p{Script=Greek}/u;
reGreekSymbol.test("π"); // true
2
此特性可以避免使用特定 Unicode 区间来进行内容类型判断,提升可读性和可维护性。
# 模板字符串
之前,
\\u开始一个 unicode 转义,\\x开始一个十六进制转义,\后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如 Windows 文件路径C:\\uuu\\xxx\111。更多细节参考模板字符串。
# es2017
- async/await
- Object.values()
- Object.entries()
- String padding: padStart()和 padEnd(),填充字符串达到当前长度
- 函数参数列表结尾允许逗号
- Object.getOwnPropertyDescriptors()
- ShareArrayBuffer 和 Atomics 对象,用于从共享内存位置读取和写入
- Atomics 对象
# aa
es2018 引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了 next()方法返回一个 Promise。因此
await可以和 for...of循环一起使用,以串行的方式运行异步操作。
async function process(array) {
for await (let i of array) {
doSomething(i);
}
}
2
3
4
5
# ov
Object.values()是一个与Object.keys()类似的新函数,但返回的是Object自身属性的所有值,不包括继承的值。
- 不使用 Object.values() :ES7
const vals = Object.keys(obj).map((key) => obj[key]);
console.log(vals);
2
- 使用 Object.values() :ES8
const values = Object.values(obj1);
console.log(values);
2
# oe
Object.entries()函数返回一个给定
对象自身可枚举属性的键值对的数组。
- 不使用 Object.entries() :ES7
Object.keys(obj).forEach((key) => {
console.log("key:" + key + " value:" + obj[key]);
});
2
3
- 使用 Object.entries() :ES8
for (let [key, value] of Object.entries(obj1)) {
console.log(`key: ${key} value:${value}`);
}
2
3
# 填充字符串
在 ES8 中 String 新增了两个实例函数
String.prototype.padStart和String.prototype.padEnd,允许将空字符串或其他字符串添加到原始字符串的开头或结尾。
# padStart
String.padStart(targetLength, [padString]);
targetLength:当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。padString:(可选)填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为 " "。
//使用示例
console.log("0.0".padStart(4, "10")); //10.0
console.log("0.00".padStart(5, "23")); //20.00,此时之保留了2
console.log("0.0".padStart(20)); // 0.00
console.log("0.00".padStart(30)); // 0.00
console.log("0.00".padStart(30).length); //30
2
3
4
5
6
7
# padEnd
String.padEnd(targetLength,padString])
targetLength:当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。padString:(可选) 填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为 " "(空格);
"0.0".padEnd(5, "2345"); //0.023
"0.0".padEnd(12, "2"); //0.0222222222
"0.0".padEnd(12).length; //,0.0 ,12
2
3
# 逗号结尾
主要作用是方便使用 git 进行多人协作开发时修改同一个函数减少不必要的行变更。
# og
Object.getOwnPropertyDescriptors()函数用来获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
const obj = { age: 12, name: "ela" };
Object.getOwnPropertyDescriptors(obj);
2
age: configurable: true;
enumerable: true;
value: 12;
writable: true;
name: configurable: true;
enumerable: true;
value: "ela";
writable: true;
2
3
4
5
6
7
8
# 共享
SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,它们都可以用来在共享内存(shared memory)上创建视图。与 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分离。
/**
*
* @param {*} length 所创建的数组缓冲区的大小,以字节(byte)为单位。
* @returns {SharedArrayBuffer} 一个大小指定的新 SharedArrayBuffer 对象。其内容被初始化为 0。
*/
new SharedArrayBuffer(length);
2
3
4
5
6
# Atomics
Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。
这些原子操作属于 Atomics 模块。与一般的全局对象不同,Atomics 不是构造函数,因此不能使用 new 操作符调用,也不能将其当作函数直接调用。Atomics 的所有属性和方法都是静态的(与 Math 对象一样)。
多个共享内存的线程能够同时读写同一位置上的数据。原子操作会确保正在读或写的数据的值是符合预期的,即下一个原子操作一定会在上一个原子操作结束后才会开始,其操作过程不会中断。
Atomics.add()
将指定位置上的数组元素与给定的值相加,并返回相加前该元素的值。
Atomics.and()
将指定位置上的数组元素与给定的值相与,并返回与操作前该元素的值。
Atomics.compareExchange()
如果数组中指定的元素与给定的值相等,则将其更新为新的值,并返回该元素原先的值。
Atomics.exchange()
将数组中指定的元素更新为给定的值,并返回该元素更新前的值。
Atomics.load()
返回数组中指定元素的值。
Atomics.or()
将指定位置上的数组元素与给定的值相或,并返回或操作前该元素的值。
Atomics.store()
将数组中指定的元素设置为给定的值,并返回该值。
Atomics.sub()
将指定位置上的数组元素与给定的值相减,并返回相减前该元素的值。
Atomics.xor()
将指定位置上的数组元素与给定的值相异或,并返回异或操作前该元素的值。
Atomics.wait()
检测数组中某个指定位置上的值是否仍然是给定值,是则保持挂起直到被唤醒或超时。返回值为 "ok"、"not-equal" 或 "time-out"。调用时,如果当前线程不允许阻塞,则会抛出异常(大多数浏览器都不允许在主线程中调用 wait())。
Atomics.wake()
唤醒等待队列中正在数组指定位置的元素上等待的线程。返回值为成功唤醒的线程数量。
Atomics.isLockFree(size)
可以用来检测当前系统是否支持硬件级的原子操作。对于指定大小的数组,如果当前系统支持硬件级的原子操作,则返回 true;否则就意味着对于该数组,Atomics 对象中的各原子操作都只能用锁来实现。此函数面向的是技术专家。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# es2016
es2016 添加了两个小的特性来说明标准化过程:
- 数组
includes()方法,用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false。 a ** b指数运算符,它与Math.pow(a, b)相同。- 模板字符串(Template string)
自 ES7 起,带标签的模版字面量遵守以下转义序列的规则:- Unicode 字符以"
\u"开头,例如\u00A9 - Unicode 码位用"
\u{}"表示,例如\u{2F804} - 十六进制以"
\x"开头,例如\xA9 - 八进制以
""和数字开头,例如\251
- Unicode 字符以"
# es2015
ES6 的特性比较多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化。两个发布版本之间时间跨度很大,所以 ES6 中的特性比较多。
- es2015 类
- es2015 模块化
- 箭头(Arrow)函数
- es2015 函数参数默认值
- es2015 模板字符串
- es2015 解构赋值
- es2015 延展操作符
- es2015 对象属性简写
- es2015Promise
- es2015Let 与 Const
# es2015 类
对熟悉 Java,object-c,c#等纯面向对象语言的开发者来说,都会对 class 有一种特殊的情怀。ES6 引入了 class(类),让 JavaScript 的面向对象编程变得更加简单和易于理解。
# es2015 模块化
模块的功能主要由
export和import组成。每一个模块都有自己单独的作用域,模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过 import 来引用其它模块提供的接口。同时还为模块创造了命名空间,防止函数的命名冲突。
# 导出(export)
ES6 允许在一个模块中使用 export 来
导出多个变量或函数。
- 导出变量
export var name ='Rainbow'
心得:ES6 不仅支持变量的导出,也支持常量的导出。
export const sqrt =Math.sqrt;//导出常量
- ES6 将一个文件视为一个模块,上面的模块通过 export 向外输出了一个变量。一个模块也可以同时往外面输出多个变量。
var name = "Rainbow";
var age = "24";
export { name, age };
2
3
- 导出函数
export function myModule(someArg) {
return someArg;
}
2
3
# 导入(import)
定义好模块的输出以后就可以在另外一个模块通过 import 引用。
import { myModule } from "myModule";
import { name, age } from "test";
2
# es2015 箭头
这是 ES6 中最令人激动的特性之一。
=>不只是关键字 function 的简写,它还带来了其它好处。
箭头函数与包围它的代码共享同一个 this,能帮你很好的解决 this 的指向问题。
有经验的 JavaScript 开发者都熟悉诸如var self = this;或var that =this这种引用外围 this 的模式。但借助 =>,就不需要这种模式了。
# 箭头函数的结构
箭头函数的箭头
=>之前是一个空括号、单个的参数名、或用括号括起的多个参数名,而箭头之后可以是一个表达式(作为函数的返回值),或者是用花括号括起的函数体(需要自行通过 return 来返回值,否则返回的是 undefined)。
- 心得:
不论是箭头函数还是bind,每次被执行都返回的是一个新的函数引用,因此如果你还需要函数的引用去做一些别的事情(譬如卸载监听器),那么你必须自己保存这个引用。
# es2015 函数参数默认值
function foo(height = 50, color = 'red')函数默认值,不仅能是代码变得更加简洁而且能规避一些问题。
# es2015 模板字符串
ES6 支持 模板字符串,使得字符串的拼接更加的简洁、直观。
var name = "Your name is " + first + " " + last + ".";
var name = `Your name is ${first} ${last}.`;
2
在 ES6 中通过 ${}就可以完成字符串的拼接,只需要将变量放在大括号之中。
# es2015 解构赋值
解构赋值语法是 JavaScript 的一种表达式,可以方便的从数组或者对象中快速提取值赋给定义的变量。
# es2015 延展操作符
延展操作符
...可以在函数调用/数组构造时, 将数组表达式或者 string 在语法层面展开;还可以在构造对象时, 将对象表达式按 key-value 的方式展开。
let objClone = { ...obj };
# 在 React 中的应用
通常我们在封装一个组件时,会对外公开一些 props 用于实现功能。大部分情况下在外部使用都应显示的传递 props 。但是当传递大量的 props 时,会非常繁琐,这时我们可以使用 ...(延展操作符,用于取出参数对象的所有可遍历属性) 来进行传递。
<CustomComponent name ='Jine' age ={21} />
const params = { name: 'Jine', age: 21}
<CustomComponent {...params} />
2
3
4
# es2015 对象属性简写
# es2015Promise
Promise 是异步编程的一种解决方案,比传统的解决方案 callback 更加的优雅。它最早由社区提出和实现的,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。