ES6+手册

135 阅读10分钟

image.png image.png image.png image.png image.png

前言

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以后的回调函数。回调函数的参数,则是一个具有valuedone两个属性的对象。

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(大整数),可以表示大于253次方-1的整数,只用来表示整数,没有位数的限制,可以进行科学和金融方面的精确计算。这是 ECMAScript 的第八种数据类型。

const a = Number.MAX_SAFE_INTEGER
const b = 9007199254740991n
// 10和b类型不一致不能相加,要先通过BigInt()函数转换
const c  = BigInt(10) + b 

2.空值合并运算(??)

只有运算符左侧的值为nullundefined时,才会返回右侧的值。

const headerText = response.settings.headerText ?? 'Hello, world!';

3.可选链(?.)

直接在链式调用的时候判断,左侧的对象是否为nullundefined。如果是的,就不再往下运算,而是返回undefined

const firstName = message?.body?.user?.firstName || 'default';

4.globalThis

任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this

5.动态import

动态加载模块,返回一个 Promise 对象。import()类似于 Node.jsrequire()方法,区别主要是前者是异步加载,后者是同步加载。

// 配合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)。
  • 不能两个或两个以上的分隔符连在一起。
  • 小数点的前后不能有分隔符。
  • 科学计数法里面,表示指数的eE前后不能有分隔符。

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环境。

  1. 当js文件处于vue项目中,并使用浏览器查看。此时可使用的es特性依赖项目使用的babel版本及支持的特性插件。 查看vue项目可使用的es特性,打开node_modules -> @babel -> preset-env -> package.json文件查看相关插件。点击查看官方可用插件。

image.png

举个例子: 如@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);

浏览器查看结果: image.png

  1. 当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);

浏览器查看结果:

image.png

  1. 当js文件不处于vue项目中,直接使用node命令执行。此时可使用的es特性依赖node的版本。 当前环境使用12.22.12版本,执行后控制台报错。

node执行结果: image.png

此时你需要升级node版本。点击查看不同node版本支持的es特性

image.png image.png

附录

  1. 权威文档:《ECMAScript 6 入门教程》
  2. 博客文档:ES6教程
  3. 官方文档:babel-plugin-list
  4. 官方文档:Node.js ES2020 Support
  5. 视频:【coderwhy新课】JS高级进阶 ES6~ES13 详解