ES6(2015)
let 和 const
ES6常用语法
1 2 3
| let name = '小豪'; const arr = []; arr[0] = '1'
|
模板字符串
1 2
| const name = "小豪"; const str = `Your name is ${name}`;
|
解构赋值
1 2 3
| let a = 1, b = 2; [a, b] = [b, a];
|
类(class)
Class 和普通构造函数有何区别?
1 2 3 4 5 6 7 8 9 10
| class Man { constructor(name) { this.name = "小豪"; } console() { console.log(this.name); } } const man = new Man("小豪"); man.console();
|
模块化(ES Module)
1 2 3 4 5
| export const sub = (a, b) => a + b;
import { sub } from "./A"; console.log(sub(1, 2));
|
Promise
Promise 对象
1 2 3 4 5
| Promise.resolve().then(() => { console.log(2); }); console.log(1);
|
Rest/Spread 属性
扩展运算符/延展操作符
在 ES6 中只针对于数组
1
| let a = [..."hello world"];
|
1 2 3 4 5
| const arr1 = [1, 2, 4]; const arr2 = [4, 5, 7]; const arr3 = [7, 8, 9];
const arr = [...arr1, ...arr2, ...arr3][(1, 2, 4, 4, 5, 7, 7, 8, 9)];
|
函数
箭头(Arrow)函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const fn = () => {};
const fn = (name) => {};
const fn = (name) => { return 2 * name; };
const fn = (name) => 2 * name;
const fn = (name) => ({ name: name });
|
函数参数默认值
函数参数默认值只有在传入参数为 undefined 的时候有效
1 2 3
| function foo(age = 25) { }
|
剩余参数
大家可能遇到过这种问题,一个函数,传入参数的个数是不确定的,这就可以用 ES6 的剩余参数
1 2 3 4 5 6
| function fn(name, ...params) { console.log(name); console.log(params); } fn("zs", 1, 2); fn("zs", 1, 2, 3, 4, 5);
|
Array 的扩展
Array 对象
Array.prototype.forEach
Array.prototype.map
Array.prototype.filter
Array.prototype.some
Array.prototype.every
Array.prototype.reduce
find 和 findIndex
对象
属性名表达式
ES6 允许字面量定义对象时,用(表达式)作为对象的属性名,即把表达式放在方括号内。
1 2 3 4 5 6
| let propKey = "foo";
let obj = { [propKey]: true, ["a" + "bc"]: 123 };
|
表达式还可以用于定义方法名
1 2 3 4 5 6 7
| let obj = { ["h" + "ello"]() { return "hi"; } };
obj.hello();
|
注意 属性名表达式与简洁表示法,不能同时使用,会报错
1 2 3 4 5 6 7 8
| const foo = 'bar'; const bar = 'abc'; const baz = { [foo] };
const foo = 'bar'; const baz = { [foo]: 'abc'};
|
注意 属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object]
1 2 3 4 5 6 7 8 9
| const keyA = { a: 1 }; const keyB = { b: 2 };
const myObject = { [keyA]: "valueA", [keyB]: "valueB" };
myObject;
|
[keyA]和[keyB]得到的都是[object Object],所以[keyB]会把[keyA]覆盖掉,而 myObject 最后只有一个[object Object]属性
Object.keys
1 2 3 4 5 6 7 8
| const obj = { name: "zs", age: 22, gender: "男" };
const keys = Object.keys(obj); console.log(keys);
|
对象属性同名简写
1 2 3 4 5 6 7 8 9 10
| const name = "zs"; const age = "22";
const obj = { name, age };
console.log(obj);
|
for of 和 for in
- for in :遍历方法,可遍历对象和数组
- for of :遍历方法,只能遍历数组,不能遍历非 iterable 对象
for of
1 2 3 4
| for (let item of arr) { console.log(item); }
|
for in
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const obj = { name: "林三心", age: 22, gender: "男" }; const arr = [1, 2, 3, 4, 5];
for (let key in obj) { console.log(key); } name; age; gender;
for (let index in arr) { console.log(index); }
|
Set 和 Map
Set 和 Map 数据结构
Set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const set1 = new Set(); set1.add(1); set1.add(2); console.log(set1);
const set2 = new Set([1, 2, 3]);
set2.add(4); set2.add("林三心"); console.log(set2);
console.log(set2.has(2));
console.log(set2.size);
set2.delete(2); console.log(set2);
|
1 2 3 4 5 6 7 8
| const set1 = new Set([1]); set1.add(1); console.log(set1);
const set2 = new Set([1, 2, "林三心", 3, 3, "林三心"]); console.log(set2);
|
- Set 的不重复性中,要注意引用数据类型和 NaN
1 2 3 4 5 6 7 8 9 10 11 12
| const set1 = new Set([1, { name: "林三心" }, 2, { name: "林三心" }]); console.log(set1);
const obj = { name: "林三心" }; const set2 = new Set([1, obj, 2, obj]); console.log(set2);
const set = new Set([1, NaN, 1, NaN]); console.log(set);
|
1 2 3 4 5
| const arr = [1, 2, 3, 4, 4, 5, 5, 66, 9, 1];
const quchongArr = [...new Set(arr)]; console.log(quchongArr);
|
Map
Map 对比 object 最大的好处就是,key 不受类型限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const map1 = new Map();
map1.set(true, 1); map1.set(1, 2); map1.set("哈哈", "嘻嘻嘻"); console.log(map1);
console.log(map1.has("哈哈"));
console.log(map1.get(true));
map1.delete("哈哈"); console.log(map1);
const map2 = new Map([ [true, 1], [1, 2], ["哈哈", "嘻嘻嘻"] ]); console.log(map2);
|
ES7(2016)
Array.prototype.includes()
传入元素,如果数组中能找到此元素,则返回 true,否则返回 false
指数操作符/求幂运算符
1 2 3 4 5
| const num = Math.pow(2, 10);
2 ** 10;
|
ES8(2017)
async/await
以同步方式执行异步操作
1 2 3 4
| async function getData() { const res = await api.getTableData(); }
|
Object.values()
用来获取对象的 value 的集合
1
| Object.values({ a: 1, b: 2, c: 3 });
|
Object.entries()
用来获取对象的键值对集合
1
| Object.entries({ a: 1, b: 2, c: 3 });
|
String padding
1 2 3 4
| "hello".padStart(10);
"hello".padEnd(10);
|
函数参数列表结尾允许逗号
Object.getOwnPropertyDescriptors()
获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
SharedArrayBuffer 对象
SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区。
1 2 3 4 5
|
new SharedArrayBuffer(10);
|
Atomics 对象
Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。
ES9(2018)
异步迭代 for await of
await 可以和 for…of 循环一起使用,以串行的方式运行异步操作
1 2 3 4 5
| async function process(array) { for await (let i of array) { } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function fn(time) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(`${time}毫秒后我成功啦!!!`); }, time); }); }
async function asyncFn() { const arr = [fn(3000), fn(1000), fn(1000), fn(2000), fn(500)]; for await (let x of arr) { console.log(x); } }
asyncFn();
|
Promise.finally()
1 2 3 4
| Promise.resolve() .then() .catch((e) => e) .finally();
|
Rest/Spread 属性
rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,在 ES9 中,为对象提供了像数组一样的 rest 参数和扩展运算符
1 2
| const values = [1, 2, 3, 5, 6]; console.log(Math.max(...values));
|
正则表达式命名捕获组
1 2
| const reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/; const match = reg.exec("2021-02-23");
|
正则表达式反向断言
1 2
| /(?=p)/,/(?<=p)/ /(?!p)/, /(?<!p>)/
|
正则表达式 dotAll 模式
正则表达式中点.匹配除回车外的任何单字符,标记 s 改变这种行为,允许行终止符的出现
1
| /hello.world/.test("hello\nworld");
|
ES10(2019)
BigInt
BigInt
是 ES10 新加的一种 JavaScript 数据类型,用来表示表示大于 2^53 - 1 的整数,2^53 - 1 是 ES10 之前,JavaScript 所能表示最大的数字
1 2 3 4 5 6 7 8 9 10 11 12 13
| const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
const hugeString = BigInt("9007199254740991");
const hugeHex = BigInt("0x1fffffffffffff");
const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111");
|
1 2
| const bigNum = BigInt(1728371927189372189739217); console.log(typeof bigNum);
|
Array.flat()和 Array.flatMap()
1 2 3 4
| [(1, 2, [3, 4])].flat(Infinity);
[(1, 2, 3, 4)].flatMap((a) => [a ** 2]);
|
String.trimStart()和 String.trimEnd()
去除字符串首尾空白字符
String.prototype.matchAll
matchAll()为所有匹配的匹配对象返回一个迭代器
1 2
| const raw_arr = "test1 test2 test3".matchAll(/t(e)(st(\d?))/g); const arr = [...raw_arr];
|
Symbol.prototype.description
只读属性,回 Symbol 对象的可选描述的字符串。
1
| Symbol("description").description;
|
Object.fromEntries()
返回一个给定对象自身可枚举属性的键值对数组
1 2 3 4 5 6
| const map = new Map([ ["foo", "bar"], ["baz", 42] ]); console.log(Object.fromEntries(map));
|
可选 Catch
ES11(2020)
Nullish coalescing Operator(空值处理)
表达式在 ?? 的左侧 运算符求值为 undefined 或 null,返回其右侧。
1 2 3 4 5 6 7 8 9 10 11
| let user = { u1: 0, u2: false, u3: null, u4: undefined, u5: "" }; let u2 = user.u2 ?? "用户2"; let u3 = user.u3 ?? "用户3"; let u4 = user.u4 ?? "用户4"; let u5 = user.u5 ?? "用户5";
|
??
和||
最大的区别是,在??这,只有 undefined 和 null 才算假值
1 2 3 4 5
| const a = 0 ?? "zs"; const b = "" ?? "zs"; const c = false ?? "zs"; const d = undefined ?? "zs"; const e = null ?? "zs";
|
Optional chaining(可选链)
?.用户检测不确定的中间节点
1 2 3
| let user = {}; let u1 = user.childer.name; let u1 = user.childer?.name;
|
Promise.allSettled
返回一个在所有给定的 promise 已被决议或被拒绝后决议的 promise,并带有一个对象数组,每个对象表示对应的 promise 结果
1 2 3 4 5 6 7 8
| 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); });
|
import()
import语句、import()、require() 用法和区别
按需导入
- 动态 import()提供基于 Promise 的 API
- import()遵循 ES 模块规则:单例、匹配符、CORS 等
- import()可以在传统的脚本中使用也可以在模块中使用
- 在代码中使用的 import()的顺序与它们被解析的顺序没有任何关联
语法
1
| import("./specifier.js");
|
这是一个从静态到动态导入转换的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import "./a.js";
import b from "./b.js"; b();
import { c } from "./c.js"; c();
import("./a.js").then(() => { console.log("a.js is loaded dynamically"); });
import("./b.js").then((module) => { const b = module.default; b("isDynamic"); });
import("./c.js").then(({ c }) => { c("isDynamic"); });
|
新基本数据类型 BigInt
任意精度的整数
globalThis
浏览器:window
worker:self
node:global
ES12(2021)
replaceAll
返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉
1 2
| const str = "hello world"; str.replaceAll("l", "");
|
Promise.any
E12 新增的 Promise 的方法
- 接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
- 如果有一个 Promise 成功,则返回这个成功结果
- 如果所有 Promise 都失败,则报错
1 2 3 4 5 6 7 8 9 10
| 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); });
|
WeakRefs
使用 WeakRefs 的 Class 类创建对对象的弱引用(对对象的弱引用是指当该对象应该被 GC 回收时不会阻止 GC 的回收行为)
逻辑运算符和赋值表达式
逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而 JavaScript 已存在的 复合赋值运算符有:
- 或等于(||=) a ||= b 等同于 a || (a = b);
- 且等于(&&=) a &&= b 等同于 a && (a = b);
1 2 3 4 5 6 7 8 9 10 11
| a ||= b;
a = a || (a = b);
a &&= b;
a = a && (a = b);
a ??= b;
a = a ?? (a = b);
|
数字分隔符
数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性
1 2 3 4 5
| const money = 1_000_000_000;
const money = 1000000000;
1_000_000_000 === 1000000000;
|
ES13(2022)
class 新特性
示例
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 39
| class Person { state = { name: "haha", age: 18 }; sex = "男";
#play = "打游戏"; playGame() { return this.#play; }
static #watch = "看电视"; static watchTv() { return this.#watch; }
static obj = new Map(); static { this.obj.set("name", "无量天尊"); this.obj.set("age", 8000); } static { console.log(this.obj); }
hasObj() { return #play in this; } } let person = new Person(); person.playGame(); Person.watchTv(); person.hasObj();
|
公共实例字段
在 ES13 之前,在定义类的属性时,需要在构造函数中定义了实例字段和绑定方法
1 2 3 4 5 6 7 8 9
| class myClass { constructor() { this.count = 1; this.increment = this.increment.bind(this); } increment() { this.count += 1; } }
|
ES 13 可以使用公共实例字段,这样就简化了类的定义,使代码更加简洁、可读
1 2 3 4 5 6
| class myClass { count = 1; increment = () => { this.count += 1; }; }
|
私有实例字段
默认情况下,class 中所有属性都是公共的,可以在 class 之外进行修改,例如
1 2 3 4 5 6 7 8 9
| class myClass { count = 1; setCount = () => { this.count += 1; }; } const es13 = new myClass(); es13.count = 5;
|
通过上面的例子可以看到,当我们直接设置 count 属性的时候,是直接跳过 setCount 进行设置的,有时候我们并不想这样,所以可以使用私有实例字段,用法很简单,只需要在私有字段添加 # 就可以实现,当然了,在调用的时候我们也应该加上 # 进行调用,如下:
1 2 3 4 5 6 7 8 9 10 11
| class myClass { #count = 1; setCount = () => { this.#count += 1; }; } const es13 = new myClass(); es13.setCount();
es13.#count = 5;
|
可以看到,当我们直接修改私有属性之后,浏览器直接抛出错误:Uncaught SyntaxError: Private field '#count' must be declared in an enclosing class
私有方法
都有私有属性了,怎么能少了私有方法呢,方法和属性一下只有加上 #
即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class myClass { #count = 1; #setCount = () => { this.#count += 1; }; newSetCount = () => { this.#setCount(); }; } const es13 = new myClass(); es13.#setCount();
es13.newSetCount();
|
私有 in 操作符
我们知道 in
操作符可以帮我们判断实例中是否存在属性,当新增了私有化类元素后,我们也可以和下面例子一样,在类定义内使用 in
操作符判断私有化类元素存在与否。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class ClassA { #privateProperty;
static hasPrivateProperty(instance) { return #privateProperty in instance; }
constructor(privateProperty) { this.#privateProperty = privateProperty; } }
const instance = new ClassA("private-property"); ClassA.hasPrivateProperty(instance);
|
静态公共字段、静态私有字段、静态私有方法
与私有实例字段和方法一样,静态私有字段和方法也使用哈希#
前缀来定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class myClass { static color = "blue"; static #count = 1; static #setCount = () => { this.#count += 1; }; newSetCount = () => { this.#setCount(); }; } const es13 = new myClass();
es13.newSetCount();
|
私有静态字段有一个限制:只有定义私有静态字段的类才能访问该字段。这可能在使用 this 时导致出乎意料的情况, 所有我们需要改一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class myClass { static #count = 1; static #setCount = () => { myClass.#count += 1; }; newSetCount = () => { myClass.#setCount(); }; } const es13 = new myClass(); es13.newSetCount();
|
类静态块
在以前,如果我们希望在初始化期间像 try…catch 一样进行异常处理,就不得不在类之外编写此逻辑。该规范就提供了一种在类声明/定义期间评估静态初始化代码块的优雅方法,可以访问类的私有字段。
ES13 之前
1 2 3 4 5 6 7 8 9 10 11 12
| class Person { static EEEOR = "error" static SUCCESS_TYPE = "success_type"; constructor() { } try { } catch { } }
|
上面代码直接报错:Uncaught SyntaxError: Unexpected token '{'
ES13 : 直接将 try...catch
使用 static
包裹起来即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Person { static EEEOR = "error"; static SUCCESS_TYPE = "success_type"; constructor() { } static { try { } catch { } } }
|
顶级模块中的 await
以往我们总是在异步函数体内使用 await 表达式,自 ES13 后,我们也可以在一个模块的顶层使用它了。
在模块顶层使用 await 表达式
1 2 3 4 5 6
| import { fetchPerson } from "@/services/person"; const person = await fetchPerson();
await import("your-component-path");
|
在异步函数中使用 await 表达式
1 2 3 4 5 6
| import { get } from "@/utils/request";
export async function getPerson() { const person = await get("/person"); return person; }
|
错误用法
在以下几种语句内的 Formal Parameters 中使用 Await 表达式,会引发静态句法的语义错误。
- 异步函数声明 (
AsyncFunctionDeclaration
) - 异步函数表达式 (
AsyncFunctionExpression
) - 异步生成器函数表达式 (
AsyncGeneratorExpression
) - Await 表达式内部 (
AwaitExpression
)
特殊情况
在早期,await 没有出现在关键字中,为了兼容旧代码,在一些特殊的 await 作为参数出现的情况下,它会被解释为标识符。如下所述:
- 当 await 出现在
AsyncFunctionBody
和 AsyncFunctionDeclaration、AsyncFunctionExpression、AsyncGeneratorDeclaration、AsyncGeneratorExpression
的 FormalParameter 外部时,await 会被解析为标识符。 - 当 await 出现在
FunctionExpression
, GeneratorExpression
, AsyncGeneratorExpression
的 BindingIdentifier
中时,await 会被解析为标识符。
error.cause
在以往 Error 对象只能传递消息信息,现在 ES13 为 Error 增添了 cause 属性,它可以是任意数据类型,方便我们获取更多地错误信息。
1 2 3 4 5 6 7 8 9 10
| try { throw new Error("faied message", { cause: "cause" }); } catch (error) { console.log(error.cause); }
|
String/Array/TypedArrays 增加 .at()
我们利用下标访问数组元素时,下标会被转换为字符串,负数下标也对应着一个独立的数组元素,所以 Js 的数组是不支持关联访问的。ES13 新增了 at 方法,使得我们可以利用负数索引进行关联访问
例如以下示例,我们可以利用 at 方法和负数索引 -2 来访问倒数第二个元素。
1 2 3
| const array = [5, 12, 8, 130, 44];
array.at(-2);
|
正则表达式匹配索引
我们常用正则表达式来处理字符串,正则表达式有很多标志,它决定了正则状态机的行为和输出结果。
ES13 新增的 d 标志对应正则实例的 hasIndices
属性,当设置 d 标志时,hasIndices
为 true 。
使用 d 标志后,正则表达式的匹配结果将包含每个捕获组捕获子字符串的开始和结束游标。
1 2 3 4 5 6 7 8 9 10
| const str = "today is saturday";
const reg = /saturday/d;
const [start, end] = reg.exec(str).indices[0];
console.log([start, end]);
|
Object.hasOwn(obj, propKey)
ES13 新增了 Object.hasOwn 方法判断这个实例上是否有属性,以代替之前的 Object.prototype.hasOwnProperty 方法。
一种安全的方式来检查对象是否具有自己的非继承的属性
1 2 3 4 5 6 7
| const object = { count: 1 };
Object.hasOwn(object, "count"); Object.hasOwn(object, "toString"); Object.hasOwn(object, "property");
|