1. ES6 (ECMAScript 2015)
1.1. let和const
let
定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
const
用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
{
var a = 1
let b = 2
const c = 3
}
console.log(a) // 1
console.log(b) // Uncaught ReferenceError: b is not defined
console.log(c) // Uncaught ReferenceError: c is not defined
const d = 4
d = 5 // Uncaught TypeError: Assignment to constant variable.
注意:const定义的对象属性是否可以改变?
可以,因为对象是引用类型,const仅保证指向对象的指针不发生改变,而修改对象的属性不会改变对象的指针,所以是被允许的
1.2. 类 (class)
class Bird {
constructor(name) {
this.name = name;
}
fly() {
console.log('fly...');
}
}
const eagle = new Bird('Eagle')
eagle.fly() // fly...
1.3. 模块化(ES Module)
支持通过import
引入类和方法
// A.js
export const add = (a, b) => a + b
// B.js
import { add } from './A'
console.log(add(1, 2)) // 3
1.4. 箭头函数 (Arrow Function)
const add = (a, b) => a + b;
add(1, 2) // 3
1.5. 函数参数默认值
function func(name = 'danny',age = 23){
// code
}
1.6. 模板字符串 (Template String)
通过反引号`
构造模板字符串
let name = 'danny'
let age = 23
let str = `My name is ${name},I am ${age} years old.`
console.log(str) // My name is danny,I am 23 years old.
1.7. 解构赋值(Destructuring)
通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。
let arr = [0,1,2]
let [a,b,c] = arr
console.log(a) // 0
console.log(b) // 1
console.log(c) // 2
let {name,age} = {name:'danny',age:23}
console.log(name) // danny
console.log(age) // 23
let [a, b, c, d, e] = 'hello';
console.log(a) // a = 'h'
console.log(b) // b = 'e'
console.log(c) // c = 'l'
console.log(d) // d = 'l'
console.log(e) // e = 'o'
1.8. 延展操作符(Spread Operator)
let a = [...'hello world']; // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d"]
1.9. 对象属性简写
let name = 'danny';
let person = {
name: name;
};
//等同于
let name = 'danny';
let person = {
name;
};
1.10. Promise
防止回调地狱
// 声明一个Promise对象
let promise = new Promise(function(resolve, reject) {
// ... some code
if (success){
resolve(value);
} else {
reject(error);
}
});
promise.then(
() => { console.log('this is success callback') }
).catch(
(err) => { console.log(err) }
).then(
...
).catch(
...
)
2. ES7 (ECMAScript 2016)
2.1. Array.prototype.includes()
["a","b"].includes("a") // true
["a","b"].includes("c") // false
2.2. 指数操作符
2**10; // 1024
3. ES8 (ECMAScript 2017)
3.1.async/await
async getData(){
const res = await api.getTableData(); // await 异步任务
// do something
}
3.2. Object.values()
Object.values({a: 1, b: 2, c: 3}); // [1, 2, 3]
3.3. Object.entries()
Object.entries({a: 1, b: 2, c: 3}); // [["a", 1], ["b", 2], ["c", 3]]
3.4. String padding
// padStart
'hello'.padStart(10); // " hello"
// padEnd
'hello'.padEnd(10) "hello "
3.5. 自动忽略函数参数列表/对象字面量/数组结尾处逗号
function foo(param1, param2,) {}
let obj = {first: 'Jane',last: 'Doe',}
let arr = ['red','green', 'blue',]
3.6. Object.getOwnPropertyDescriptors()
返回指定对象所有自身属性(非继承属性)的描述对象。
该方法的引入目的,主要是为了解决Object.assign()
无法正确拷贝 get 属性和 set 属性的问题。
const clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj));
3.7. SharedArrayBuffer
SharedArrayBuffer
对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer
对象,它们都可以用来在共享内存(shared memory)上创建视图。与 ArrayBuffer
不同的是,SharedArrayBuffer
不能被分离。
new SharedArrayBuffer(length)
3.8. Atomics
Atomics
对象提供了一组静态方法对 SharedArrayBuffer
和 ArrayBuffer
对象进行原子操作。、
4. ES9 (ECMAScript 2018)
4.1. 异步迭代
await
可以和for...of
循环一起使用,以串行的方式运行异步操作
async function process(array) {
for await (let i of array) {
// doSomething(i);
}
}
4.2. Promise.finally()
Promise.resolve().then().catch(e => e).finally();
4.3. Rest/Spread 属性
可以简单理解rest
为收集,spread
为展开
用于对象解构的rest
操作符,目前这个操作符只能在数组解构和参数定义中使用
const obj = {foo: 1, bar: 2, baz: 3};
const {foo, ...rest} = obj;
console.log(rest) // {bar: 2, baz: 3}
如果你正在使用对象解构来处理命名参数,rest 操作符让你可以收集所有剩余参数
function func(param1, param2, ...rest) { // rest operator
console.log('All parameters: ',param1, param2, ...rest); // spread operator
}
fuc(1,2,3,4,5) // All parameters: 1 2 3 4 5
对象字面量中的 spread
操作符,目前这个操作符只能用于数组字面量和在函数方法中调用。
const obj = {foo: 1, bar: 2, baz: 3};
{...obj, qux: 4} // { foo: 1, bar: 2, baz: 3, qux: 4 }
4.4. 正则表达式s标记(dotAll)
ES9之前,在正则中.
可以匹配任意字符,除了换行符\n
。
/hello.es9/.test('hello\nes9') // false
/hello.es9/.test('hello\tes9') // true
ES9引入了dotAll模式,通过使用标记s选项,.就可以匹配换行符。
/hello.es9/s.test('hello\nes9') // true
4.5. 正则表达式命名捕获组(Named capture groups)
ES9之前,正则表达式中小括号匹配的分组是通过索引编号的
const reg = /(\d{4})-(\d{2})-(\d{2})/u;
const matched = reg.exec('2021-01-05');
matched[0]; // 2021-01-05
matched[1]; // 2021
matched[2]; // 01
matched[3]; // 05
ES9允许命名捕获组使用符号?<name>
,可以指定小括号中匹配内容的名称放在groups里,这样可以提高代码的可读性。
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const matched = reg.exec('2021-01-05');
matched.groups.year; // 2021
matched.groups.month; // 01
matched.groups.day; // 05
//命名捕获组也可以使用在replace()方法中。例如将日期转换为“年月日”格式:
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
'2018-12-31'.replace(reg, '$<year>年$<month>月$<day>日'); // 2018年12月31日
4.6. 反向断言 (lookbehind)
(?=pattern):正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。
(?!pattern):正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。
(?<=pattern): 反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。
(?<!pattern):反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
4.7. 正则表达式 Unicode 转义
Unicode属性转义,形式为\p{...}和\P{...},在正则表达式中使用标记u(unicode) 选项。
/^\p{White_Space}+$/u.test(' ') // 匹配空格
/^\p{Script=Greek}+$/u.test('μετά') // 匹配希腊字母
/^\p{Script=Latin}+$/u.test('Grüße') // 匹配拉丁字母
/^\p{Surrogate}+$/u.test('\u{D83D}') // 匹配单独的替代字符
5. ES10 (ECMAScript 2019)
5.1. Array.flat()和Array.flatMap()
flat()
[1, 2, [3, 4]].flat(Infinity); // [1, 2, 3, 4]
flatMap()
[1, 2, 3, 4].flatMap(a => [a**2]); // [1, 4, 9, 16]
5.2. String.trimStart()和String.trimEnd()
去除字符串首尾空白字符
' hello'.trimStart() // 'hello'
'hello '.trimEnd() // 'hello'
5.3. String.prototype.matchAll
matchAll()
方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。
const regexp = /t(e)(st(\d?))/g;
const str = 'test1test2';
const array = [...str.matchAll(regexp)];
console.log(array[0]);
// expected output: Array ["test1", "e", "st1", "1"]
console.log(array[1]);
// expected output: Array ["test2", "e", "st2", "2"]
5.4. Symbol.prototype.description
用来获取Symbol
字符串的描述
let sy = Symbol('hello world')
console.log(sy.description) // 'hello world'
5.5. Object.fromEntries()
Object.entries()
是将对象转成一个自身可枚举属性的键值对数组。同样,我们也可以通过 Object.fromEntries()
把键值对数组转成了对象。
// 将数组转成对象
const array = [['key 1', 'value 1'],['key 2', 'value 2']
Object.fromEntries(array) // { key 1: "value 1", key 2: "value 2"}
//将 Map 转成对象
const map = new Map()
map.set('key 1', 'value 1')
map.set('key 2', 'value 2')
Object.fromEntries(map) // { key 1: "value 1", key 2: "value 2"}
5.6. 可选 Catch Binding
允许开发人员在catch
块中,不使用error参数的情况下使用try/catch
// ES10 之前使用
try {
// some code
}
catch (err) {
// error handling code
}
// 现在使用ES10这样的try / catch:
try {
// some code
}
catch {
// error handling code
}
6. ES11(ECMAScript 2020)
6.1. 私有属性
使用#
修饰私有字段
class Person {
name
#age
constructor(name, age) {
this.name = name
this.#age = age
}
}
const person= new Person('danny', 22)
console.log(person.name) // danny
console.log(person.#age) // Uncaught SyntaxError: Private field '#age' must be declared in an enclosing class
6.2. Nullish coalescing Operator(空值处理)
表达式在 ??
的左侧 运算符求值为undefined或null,返回其右侧
let user = {
u1: 0,
u2: false,
u3: null,
u4: undefined
u5: '',
}
let u2 = user.u2 ?? '用户2' // false
let u3 = user.u3 ?? '用户3' // 用户3
let u4 = user.u4 ?? '用户4' // 用户4
let u5 = user.u5 ?? '用户5' // ''
6.3. 可选链(Optional chaining)
使用 ?.
检测不确定的中间节点
let user = {}
let u1 = user.childer.name // TypeError: Cannot read property 'name' of undefined
let u2 = user.childer?.name // undefined
6.4. Promise.allSettled
该Promise.allSettled()
方法返回一个在所有给定的promise
都已经fulfilled
或rejected
后的promise,并带有一个对象数组,每个对象表示对应的promise结果。
当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise
的结果时,通常使用它。
相比之下,Promise.all()
更适合彼此相互依赖或者在其中任何一个reject
时立即结束。
const promises = [
fetch('/api-1'),
fetch('/api-2'),
fetch('/api-3'),
];
await Promise.allSettled(promises)
.then((results) => results
.forEach((result) => console.log(result.status)))
6.5. 动态import
动态导入hello模块进行使用,import(路径)的得到的是一个Promise对象,使用then获取成功的模块。
// a.js
export const hello = () => {
alert('hello');
};
// b.js
const btn = document.querySelector('#btn');
btn.onclick = () => {
import('./a.js').then(module => {
module.hello();
});
};
6.6. BigInt
ES11 引入了新的数据类型 BigInt类型 大整型,进行更大的数值运算。
注意:大整型只能和大整型进行运算
// 使用方法:
// 1.在普通的整型数后加n
let n = 666n;
console.log(n, typeof n); //666n "bigint"
// 2.使用BigInt()函数
let n = 666;
console.log(BigInt(n)); // 666n
6.7. 绝对全局对象(globalThis)
字面意思为全局的this
, globalThis
始终指向全局对象(无论运行环境node、浏览器等)
console.log(globalThis) //Window {window: Window, self: Window, document: document, name: "", location: Location, …}
7. ES12(ECMAScript2021)
7.1. replaceAll
返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉
const str = 'hello world';
str.replaceAll('o', 'b'); // "hellb wbrld"
7.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);
}); // AggregateError: All promises were rejected
const promise3 = new Promise((resolve, reject) => reject('我是失败的Promise_3'));
const promise4 = new Promise((resolve, reject) => resolve('我是成功的Promise_4'));
const promiseList1 = [promise3, promise4];
Promise.any(promiseList1)
.then(values=>{
console.log(values);
})
.catch(e=>{
console.log(e);
}); // 我是成功的Promise_4
7.3. WeakRefs
使用WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)
7.4. 逻辑运算符和赋值表达式
逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的复合赋值运算符有:
操作运算符:+= -= *= /= %= **=
位操作运算符:&= ^= |=
按位运算符:<<= >>= >>>=
现有的的运算符,其工作方式都可以如此来理解
表达式:a op= b
等同于:a = a op b
逻辑运算符和其他的复合赋值运算符工作方式不同
表达式:a op= b
等同于:a = a op (a = b)
a ||= b
//等价于
a = a || (a = b)
a &&= b
//等价于
a = a && (a = b)
a ??= b
//等价于
a = a ?? (a = b)
为什么不再是跟以前的运算公式a = a op b一样呢,而是采用a = a op (a = b)。因为后者当且仅当a的值为false的时候才计算赋值,只有在必要的时候才执行分配,而前者的表达式总是执行赋值操作
7.5. 数字分隔符
数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性
const money1 = 1_000_000_000;
//等价于
const money2 = 1000000000;
money1 === money2 // true