前言
ES6模块持续更新......
一、ES6
1.Promise
点击查看独立的文章讲解。
二、ES7
1.Array.prototype.includes
检测数组中是否包含某个元素,返回相对应得布尔类型值。该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。
[1,2,3].includes(2); // true
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
2.求幂运算(**)
求幂运算,与Math.pow()效果一致。运算时右结合,多个指数运算符连用时,是从最右边开始计算的。
2 ** 2
// 4
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
指数运算符可以与等号结合,形成一个新的赋值运算符(**=)。
let b = 4;
b **= 3;
// 等同于 b = b * b * b;
三、ES8
1.Object.values
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
TODO疑问:数组的排列方式依据是?
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
如果参数是一个字符串,会返回各个字符组成的一个数组。
Object.values('foo')
// ['f', 'o', 'o']
如果参数不是对象,Object.values会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values会返回空数组。
Object.values(42) // []
Object.values(true) // []
2.Object.entries
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
- 用途1:遍历对象的属性
let obj = { one: 1, two: 2 };
for (let [k, v] of Object.entries(obj)) {
console.log(
`${JSON.stringify(k)}: ${JSON.stringify(v)}`
);
}
// "one": 1
// "two": 2
- 用途2:将对象转为真正的
Map结构
const obj = { foo: 'bar', baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }
// Map转成Object
const obj1 = Object.fromEntries(map)
obj1 // { foo: 'bar', baz: 42 }
3.Object.getOwnPropertyDescriptors
返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象。
注:引入的目的,主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。
4.String的padStart()/padEnd()
字符串补全长度的功能。padStart()用于头部补全,padEnd()用于尾部补全。接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
padStart()的常见用途:
- 用途1:为数值补全指定位数
// 生成 10 位的数值字符串
'123456'.padStart(10, '0') // "0000123456"
- 用途2:提示字符串格式
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
5.async/await
TODO待补充单独讲解文章......
四、ES9
1.Async iterators(异步遍历器)
调用遍历器的next()方法,返回的是一个 Promise 对象。因此,可以使用then方法指定,这个 Promise 对象的状态变为resolve以后的回调函数。回调函数的参数,则是一个具有value和done两个属性的对象。
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
asyncIterator
.next().then(iterResult1 => {
console.log(iterResult1); // { value: 'a', done: false }
return asyncIterator.next();
}).then(iterResult2 => {
console.log(iterResult2); // { value: 'b', done: false }
return asyncIterator.next();
}).then(iterResult3 => {
console.log(iterResult3); // { value: undefined, done: true }
});
// 等价于
async function f() {
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
console.log(await asyncIterator.next());
// { value: 'a', done: false }
console.log(await asyncIterator.next());
// { value: 'b', done: false }
console.log(await asyncIterator.next());
// { value: undefined, done: true }
}
2.对象的扩展运算符(...)
用来获取参数对象的所有可遍历属性,然后拷贝到当前的对象中。
// 对象的浅拷贝
const obj = {
a:1,
b:2
}
const obj2 = {
...obj
}
console.log(obj2);
// { a: 1, b: 2 }
// 对象合并
const a = {
name: 'hello'
}
const b = {
age: 18
}
const c = {...a, ...b}
console.log(c);
// { name: 'hello', age: 18 }
3.Promise.prototype.finally()
用于指定不管 Promise 对象最后状态如何,都会执行的操作。详见ES6-Promise类。
五、ES10
1.Array.prototype.flat()
用于将嵌套(多维)的数组“拉平”,变成一维的数组。返回一个新数组,对原数据没有影响。
[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。
[1, 2, [3, [4, 5]]].flat()
// [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2)
// [1, 2, 3, 4, 5]
如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。
[1, [2, [3]]].flat(Infinity)
// [1, 2, 3]
如果原数组有空位,flat()方法会跳过空位。可以用来对数组做去空位处理。
[1, 2, , 4, 5].flat()
// [1, 2, 4, 5]
2.Array.prototype.flatMap()
对原数组的每个成员执行一个函数,然后对返回值组成的数组执行flat()方法。比map()方法多一次flat()。
[2, 3, 4].flatMap() === [2, 3, 4].map().flat()
// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
3.Object.fromEntries
Object.entries()的逆操作,用于将一个键值对数组转为对象。
Object.fromEntries([ ['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }
特别适合将 Map 结构转为对象。
const entries = new Map([ ['foo', 'bar'],
['baz', 42]
]);
Object.fromEntries(entries)
// { foo: "bar", baz: 42 }
该方法的一个用处是配合URLSearchParams对象,将查询字符串转为对象。
Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
// { foo: "bar", baz: "qux" }
4.String的trimStart()/trimEnd()
消除字符串头/尾部的空格。
5.Symbol.prototype.description
返回 Symbol 值的描述。
六、ES11
1.BigInt数据类型
BigInt(大整数),可以表示大于2的53次方-1的整数,只用来表示整数,没有位数的限制,可以进行科学和金融方面的精确计算。这是 ECMAScript 的第八种数据类型。
const a = Number.MAX_SAFE_INTEGER
const b = 9007199254740991n
// 10和b类型不一致不能相加,要先通过BigInt()函数转换
const c = BigInt(10) + b
2.空值合并运算(??)
只有运算符左侧的值为null或undefined时,才会返回右侧的值。
const headerText = response.settings.headerText ?? 'Hello, world!';
3.可选链(?.)
直接在链式调用的时候判断,左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined。
const firstName = message?.body?.user?.firstName || 'default';
4.globalThis
任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this。
5.动态import
动态加载模块,返回一个 Promise 对象。import()类似于 Node.js 的require()方法,区别主要是前者是异步加载,后者是同步加载。
// 配合async/await使用
async function renderWidget() {
const container = document.getElementById('widget');
if (container !== null) {
// 等同于
// import("./widget").then(widget => {
// widget.render(container);
// });
const widget = await import('./widget.js');
widget.render(container);
}
}
renderWidget();
用途:
- 1.按需加载。在需要的时候,再加载某个模块。
button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
})
});
- 2.if条件加载。如果满足条件,就加载模块
A,否则加载模块B。
if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}
- 3.动态的模块路径。根据函数
f的返回结果,加载不同的模块。
import(f())
.then(...);
6.import.meta
返回当前模块的元信息。只能在模块内部使用,如果在模块外部使用会报错。
具体包含哪些属性,标准没有规定,由各个运行环境自行决定。一般来说,import.meta至少会有下面两个属性:
import.meta.url
new URL('data.txt', import.meta.url)
import.meta.scriptElement
// HTML 代码为
// <script type="module" src="my-module.js" data-foo="abc"></script>
// my-module.js 内部执行下面的代码
import.meta.scriptElement.dataset.foo
// "abc"
7.Promise.allSettled()
用来确定一组异步操作是否都结束了(不管成功或失败)。详见ES6-Promise类。
8.String.prototype.matchAll()
可以一次性取出正则表达式在字符串里面所有的匹配。返回的是一个遍历器(Iterator),而不是数组,可以用for...of循环取出。
// 转为数组的方法一
[...string.matchAll(regex)]
// 转为数组的方法二
Array.from(string.matchAll(regex))
七、ES12
1.FinalizationRegistry
用来指定目标对象被垃圾回收机制清除以后,所要执行的回调函数。
// 1.新建一个注册表实例
const registry = new FinalizationRegistry(heldValue => {
// ....
});
// 2.注册表实例的`register()`方法,用来注册所要观察的目标对象
registry.register(theObject, "some value");
theObject就是所要观察的目标对象,一旦该对象被垃圾回收机制清除,注册表就会在清除完成后,调用早前注册的回调函数,并将some value作为参数(前面的heldValue)传入回调函数。注册表不对目标对象theObject构成强引用,属于弱引用。
注:由于无法知道清理器何时会执行,所以最好避免使用它。另外,如果浏览器窗口关闭或者进程意外退出,清理器则不会运行。
2.WeakRef
创建对象的弱引用。弱引用对象的一大用处,就是作为缓存,未被清除时可以从缓存取值,一旦清除缓存就自动失效。
3.逻辑赋值运算符
先进行逻辑运算,然后根据运算结果,再视情况进行赋值运算。可以为变量或属性设置默认值。
||=&&=??=
// 或赋值运算符
x ||= y
// 等同于
x || (x = y)
// 与赋值运算符
x &&= y
// 等同于
x && (x = y)
// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)
4.数字分隔符
数值可以使用下划线(_)作为分隔符。没有指定间隔的位数,也就是说,可以每三位添加一个分隔符,也可以每一位、每两位、每四位添加一个。
123_00 === 12_300 // true
12345_00 === 123_4500 // true
12345_00 === 1_234_500 // true
注意点:
- 不能放在数值的最前面(leading)或最后面(trailing)。
- 不能两个或两个以上的分隔符连在一起。
- 小数点的前后不能有分隔符。
- 科学计数法里面,表示指数的
e或E前后不能有分隔符。
5.String.prototype.replaceAll
一次性替换所有匹配。
'aabbcc'.replaceAll('b', '_')
// 'aa__cc'
八、ES13
1.Object.hasOwn()
JavaScript 对象的属性分成两种:自身的属性和继承的属性。
hasOwnProperty():判断某个属性是否为原生属性。hasOwn():判断是否为自身的属性。
hasOwn()方法接受两个参数,第一个是所要判断的对象,第二个是属性名。
const foo = Object.create({ a: 123 });
foo.b = 456;
// 属性`a`是继承属性
Object.hasOwn(foo, 'a') // false
// 属性`b`是原生属性
Object.hasOwn(foo, 'b') // true
2.类中的新成员和私有属性
越来越Java化......
九、项目使用ES6+
已知js文件可执行的环境有浏览器和node环境。
- 当js文件处于vue项目中,并使用浏览器查看。此时可使用的es特性依赖项目使用的babel版本及支持的特性插件。 查看vue项目可使用的es特性,打开node_modules -> @babel -> preset-env -> package.json文件查看相关插件。点击查看官方可用插件。
举个例子: 如@babel/plugin-proposal-optional-chaining(可选链)
// 代码块1
const obj = {
name: {
nickName: "hello test",
},
};
// obj有name属性
console.log(obj?.name?.nickName);
// obj没有fakeName属性
console.log(obj?.fakeName?.nickName);
浏览器查看结果:
- 当js文件不处于vue项目中,并使用浏览器查看。此时可使用的es特性依赖浏览器的版本。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
// index.js
// 代码块2
const obj = {
name: {
nickName: "hello test null",
},
};
// obj有name属性
console.log(obj?.name?.nickName);
// obj没有fakeName属性
console.log(obj?.fakeName?.nickName);
浏览器查看结果:
- 当js文件不处于vue项目中,直接使用node命令执行。此时可使用的es特性依赖node的版本。 当前环境使用12.22.12版本,执行后控制台报错。
node执行结果:
此时你需要升级node版本。点击查看不同node版本支持的es特性。
附录
- 权威文档:《ECMAScript 6 入门教程》
- 博客文档:ES6教程
- 官方文档:babel-plugin-list
- 官方文档:Node.js ES2020 Support
- 视频:【coderwhy新课】JS高级进阶 ES6~ES13 详解