# ECMAScript - 新特性

// todo

ES2020-ES11 ES2020 是与 2020 年相对应的 ECMAScript 版本 String.protype.matchAll matchAll()方法返回一个正则表达式在当前字符串的所有匹配 不过,它返回的是一个遍历器(Iterator),而不是数组。遍历器转为数组是非常简单的,使用...运算符和 Array.from()方法就可以了。 const string = "test1test2test3"; const regex = /t(e)(st(\d?))/g;

const newdata = string.matchAll(regex);

for (const match of newdata) { console.log(match); } // ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"] // ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"] // ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]

// 转为数组的方法一 [...newdata];

// 转为数组的方法二 Array.from(newdata); 复制代码 详细内容参考ES 入门-matchAll Dynamic import import(specifier)函数,支持动态加载模块, import 函数的参数 specifier,指定所要加载的模块的位置。import 命令能够接受什么参数,import()函数就能接受什么参数,两者区别主要是后者为动态加载。 import()返回一个 Promise 对象 const someVariable = "user";

import(./some-modules/${someVariable}.js) .then((module) => { // 业务逻辑 module.loadPageInto(main); }) .catch((err) => { // 加载失败 }); 复制代码 详细内容参考ES 入门-import Promise.allSettled Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束 有时候,我们不关心异步请求的结果,只关心所有的请求有没有结束。这时,Promise.allSettled()方法就很有用 const promises = [fetch("index.html"), fetch("https://does-not-exist/")]; const results = await Promise.allSettled(promises);

// 过滤出成功的请求 const successfulPromises = results.filter((p) => p.status === "fulfilled");

// 过滤出失败的请求,并输出原因 const errors = results .filter((p) => p.status === "rejected") .map((p) => p.reason); 复制代码 globalThis ES2020 之前获取不同环境的this需要如下封装 const getGlobalThis = () => { // 在 webworker 或 service worker 中 if (typeof self !== "undefined") return self;

// 在浏览器中 if (typeof window !== "undefined") return window;

// 在 Node.js 中 if (typeof global !== "undefined") return global;

// 独立的 JavaScript shell if (typeof this !== "undefined") return this;

throw new Error("Unable to locate global object"); }; const theGlobalThis = getGlobalThis();

if (typeof theGlobalThis.setTimeout !== "function") { // 此环境中没有 setTimeout 方法! } 复制代码 现在,globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身) if (typeof globalThis.setTimeout !== "function") { // 此环境中没有 setTimeout 方法! } 复制代码 详细内容参考MDN-globalThis 空位合并操作符(Nullish coalescing Operator) 在 JS 中,?? 运算符被称为非空运算符。如果第一个参数不是 null/undefined(这里只有两个假值,但是 JS 中假值包含:未定义 undefined、空对象 null、数值 0、空数字 NaN、布尔 false,空字符串'',不要搞混了),将返回第一个参数,否则返回第二个参数。比如, null ?? 5; // => 5 3 ?? 5; // => 3 复制代码 给变量设置默认值时,以前常用 ||逻辑或运算符,例如, const prevMoney = 1; const currMoney = 0; const noAccount = null; const futureMoney = -1; function moneyAmount(money) { return money || 账户未开通; } console.log(moneyAmount(prevMoney)); // => 1 console.log(moneyAmount(currMoney)); // => 账户未开通 console.log(moneyAmount(noAccount)); // => 账户未开通 console.log(moneyAmount(futureMoney)); // => -1 复制代码 上面我们创建了函数 moneyAmount,它返回当前用户余额。我们使用 || 运算符来识别没有帐户的用户。然而,当用户没有帐户时,这意味着什么?将无账户视为空而不是 0 更为准确,因为银行账户可能没有(或负)货币。在上面的例子中,|| 运算符将 0 视为一个虚假值,不应该包括用户有 0 美元的帐户。让我们使用?? 非空运算符来解决这个问题: const currMoney = 0; const noAccount = null; function moneyAmount(money) { return money ?? 账户未开通; } moneyAmount(currMoney); // => 0 moneyAmount(noAccount); // => 账户未开通 复制代码 概括地说 ?? 运算符允许我们在忽略错误值(如 0 和空字符串)的同时指定默认值。 可选链操作符(Optional Chaining) ?. 也叫链判断运算符。它允许开发人员读取深度嵌套在对象链中的属性值,而不必验证每个引用。当引用为空时,表达式停止计算并返回 undefined。比如: var travelPlans = { destination: "DC", monday: { location: "National Mall", budget: 200, }, }; console.log(travelPlans.tuesday?.location); // => undefined 复制代码 现在,把我们刚刚学到的结合起来 function addPlansWhenUndefined(plans, location, budget) { if (plans.tuesday?.location == undefined) { var newPlans = { plans, tuesday: { location: location ?? "公园", budget: budget ?? 200, }, }; } else { newPlans ??= plans; // 只有 newPlans 是 undefined 时,才覆盖 console.log("已安排计划"); } return newPlans; } // 对象 travelPlans 的初始值,来自上面一个例子 var newPlans = addPlansWhenUndefined(travelPlans, "Ford 剧院", null); console.log(newPlans); // => { plans: // { destination: 'DC', // monday: { location: '国家购物中心', budget: 200 } }, // tuesday: { location: 'Ford 剧院', budget: 200 } } newPlans = addPlansWhenUndefined(newPlans, null, null); // logs => 已安排计划 // returns => newPlans object 复制代码 上面的例子包含了我们到目前为止所学的所有运算符。现在我们已经创建了一个函数,该函数将计划添加到当前没有嵌套属性的对象 tuesday.location 中。我们还使用了非空运算符来提供默认值。此函数将错误地接受像“0”这样的值作为有效参数。这意味着 budget 可以设置为零,没有任何错误。 BigInt primitive type 旧版本的 JS 标准最大的整数只能是253 - 1, 现在使用BigInt 用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 这是 ECMAScript 的又一种数据类型。 可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数 BigInt()。 const theBiggestInt = 9007199254740991n;

const alsoHuge = BigInt(9007199254740991); // ↪ 9007199254740991n 复制代码

ES 入门-BigInt

ES2019-ES10 Array#{flat,flatMap} 数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。 [1, 2, [3, 4]].flat(); // [1, 2, 3, 4] 复制代码 flatMap()只能展开一层数组。 // 相当于 [[[2]], [[4]], [[6]], [[8]]].flat() [1, 2, 3, 4].flatMap((x) => [[x * 2]]); // [[2], [4], [6], [8]] 复制代码 详细内容参考ES 入门-flat Object.fromEntries 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 }

// 例二 const map = new Map().set("foo", true).set("bar", false); Object.fromEntries(map); // { foo: true, bar: false } 复制代码 String#{trimStart,trimEnd} ES2019 对字符串实例新增了trimStart()和trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。 const s = " abc ";

s.trim(); // "abc" s.trimStart(); // "abc " s.trimEnd(); // " abc" 复制代码 Symbol#description ES2019 提供了一个实例属性description,直接返回 Symbol 的描述。 // 创建 Symbol 的时候,可以添加一个描述。 const sym = Symbol("foo");

sym.description; // "foo" 复制代码 上面代码中,sym 的描述就是字符串 foo。 try { } catch {} // optional binding 旧版本的try / catch语句中的catch子句需要一个变量。 现在可以不加了 // 旧版本 try { console.log(a); } catch (error) { console.log("报错了"); }

// ES2019-SE10 try { console.log(a); } catch { console.log("报错了"); } 复制代码 U+2028 和 U+2029 在 ES2019 之前的版本中,不接受不转义的

行分隔符U + 2028 段落分隔符U + 2029

ES2019 允许 JavaScript 字符串直接输入 U+2028(行分隔符)和 U+2029(段分隔符)。 /* ES2019之前,下面的代码会报错

ES2019 下面代码不会报错。 */ const PS = eval("'\u2029'"); 复制代码 ES 入门-U+2028 和 U+2029 JSON-stringify-的改造 为了确保返回的是合法的 UTF-8 字符,ES2019 改变了 JSON.stringify()的行为。如果遇到 0xD800 到 0xDFFF 之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。 JSON.stringify("\u{D834}"); // ""\uD834"" JSON.stringify("\uDF06\uD834"); // ""\udf06\ud834"" 复制代码 ES 入门-JSON-stringify-的改造 Array.prototype.sort() 的稳定排序 早先的 ECMAScript 没有规定,Array.prototype.sort()的默认排序算法是否稳定,留给浏览器自己决定,这导致某些实现是不稳定的。ES2019 明确规定,Array.prototype.sort()的默认排序算法必须稳定。这个规定已经做到了,现在 JavaScript 各个主要实现的默认排序算法都是稳定的。 const arr = ["peach", "straw", "apple", "spork"];

const stableSorting = (s1, s2) => { if (s1[0] < s2[0]) return -1; return 1; };

arr.sort(stableSorting); // ["apple", "peach", "straw", "spork"] 复制代码 ES 入门-排序稳定性 revised Function#toString ES2019 对函数实例的 toString()方法做出了修改。 toString()方法返回函数代码本身,以前会省略注释和空格。 function /* foo comment */ foo() {}

// 老版本 foo.toString(); // function foo() {}

// 新版 foo.toString(); // "function /* foo comment */ foo () {}" 复制代码

ES2018-ES9 解除模板字面量限制(Lifting template literal restriction). ES2018 放松了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回undefined,而不是报错,并且从raw属性上面可以得到原始字符串。 function tag(strs) { strs[0] === undefined strs.raw[0] === "\unicode and \u{55}"; } tag\unicode and \u{55} 复制代码 上面代码中,模板字符串原本是应该报错的,但是由于放松了对字符串转义的限制,所以不报错了,JavaScript 引擎将第一个字符设置为undefined,但是raw属性依然可以得到原始字符串,因此tag函数还是可以对原字符串进行处理。

ES 入门-模板字符串的限制 ES 入门-row ES 入门-修饰符:u

正则之 s 修饰符:dotAll 模式-(s (dotAll) flag for regular expressions). ES2018 引入 s 修饰符,使得.可以匹配任意单个字符。 /foo.bar/s.test("foo\nbar"); // true 复制代码 这被称为dotAll模式,即点(dot)代表一切字符。所以,正则表达式还引入了一个dotAll属性,返回一个布尔值,表示该正则表达式是否处在dotAll模式。 ES 入门-修饰符:dotAll 模式 正则之具名组匹配(RegExp named capture groups) ES2018 引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。

const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const matchObj = RE_DATE.exec("1999-12-31");
const year = matchObj.groups.year; // "1999"
const month = matchObj.groups.month; // "12"
const day = matchObj.groups.day; // "31"
1
2
3
4
5

ES 入门-修饰符:具名组匹配 Rest/Spread Properties. ES6 为数组引入了扩展运算符的写法, 在 ES2018 中,为对象也引入了此写法 const obj = { a: "a", b: "b", c: "c", d: "d", e: "e" };

// 对象结构 const { a, b, c, ...rest } = obj;

// 组成新对象 const newObj = { a, ...rest }; 复制代码 正则之后行断言(RegExp Lookbehind Assertions.) ES2018 引入后行断言 “后行断言”指: x只有不在y后面才匹配,必须写成/(?<!y)x/。比如,只匹配不在美元符号后面的数字,要写成/(?<!$)\d+/。 /(?<=$)\d+/.exec('Benjamin Franklin is on the $100 bill') // ["100"] /(?<!$)\d+/.exec('it’s is worth about €90') // ["90"] 复制代码 使用后行断言进行字符串替换。 const RE_DOLLAR_PREFIX = /(?<=$)foo/g; "$foo %foo foo".replace(RE_DOLLAR_PREFIX, "bar"); // '$bar %foo foo' 复制代码 ES 入门-后行断言 Unicode 属性类(RegExp Unicode Property Escapes) ES2018 引入了一种新的类的写法\p{...}和\P{...},允许正则表达式匹配符合 Unicode 某种属性的所有字符。 const regexGreekSymbol = /\p{Script=Greek}/u; regexGreekSymbol.test("π"); // true

// 匹配所有空格 const reg = /\p{White_Space}/;

// 匹配所有的箭头字符 const regexArrows = /^\p{Block=Arrows}+$/u; regexArrows.test("←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩"); // true 复制代码 ES 入门-Unicode 属性类 Promise.prototype.finally. finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。 promise .then(result => {···}) .catch(error => {···}) .finally(() => {···}); 复制代码 上面代码中,不管 promise 最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。 ES 入门-finally 按顺序完成异步操作(Asynchronous Iteration) 实际开发中,经常遇到一组异步操作,需要按照顺序完成。比如,依次远程读取一组 URL,然后按照读取的顺序输出结果。 async function logInOrder(urls) { // 并发读取远程URL const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); });

// 按次序输出 for (const textPromise of textPromises) { console.log(await textPromise); } } 复制代码 async function getData() { const promises = [fetch("url1"), fetch("url2"), fetch("url3"), fetch("url4")]; for (const item of promises) { // 打印出promise console.log(item); }

for await (const item of promises) { // 打印出请求的结果 console.log(item); } } 复制代码 ES 入门-顺序异步操作

ES2017-ES8 Object.values/Object.entries Object.values 方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。 const obj = { foo: "bar", baz: 42 }; Object.values(obj); // ["bar", 42]

const obj = { 100: "a", 2: "b", 7: "c" }; Object.values(obj); // ["b", "c", "a"] 复制代码 Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。 const obj = { foo: "bar", baz: 42 }; Object.entries(obj); // [ ["foo", "bar"], ["baz", 42] ] 复制代码 Object.entries 的基本用途是遍历对象的属性。 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 复制代码 Object.entries 方法的另一个用处是,将对象转为真正的 Map 结构。 const obj = { foo: "bar", baz: 42 }; const map = new Map(Object.entries(obj)); map; // Map { foo: "bar", baz: 42 } 复制代码 String padding ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。 "x".padStart(5, "ab"); // 'ababx' "x".padStart(4, "ab"); // 'abax'

"x".padEnd(5, "ab"); // 'xabab' "x".padEnd(4, "ab"); // 'xaba' 复制代码 padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。 "1".padStart(10, "0"); // "0000000001" "12".padStart(10, "0"); // "0000000012" "123456".padStart(10, "0"); // "0000123456" 复制代码 另一个用途是提示字符串格式。 "12".padStart(10, "YYYY-MM-DD"); // "YYYY-MM-12" "09-12".padStart(10, "YYYY-MM-DD"); // "YYYY-09-12" 复制代码 Object.getOwnPropertyDescriptors ES2017 引入了 Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。

value — 属性实际的值 writable — 属性的值是否可以被修改 get — 获取函数,在读取属性时调用 set — 设置函数,在写入属性时调用 configurable — 属性是否可以通过 delete 删除并重新定义,是否可以修改它的特 性,以及是否可以把它改为访问器属性 enumerable — 属性是否可以通过 for-in 循环返回

const obj = { foo: 123, get bar() { return "abc"; }, };

Object.getOwnPropertyDescriptors(obj); // { foo: // { value: 123, // writable: true, // enumerable: true, // configurable: true }, // bar: // { get: [Function: get bar], // set: undefined, // enumerable: true, // configurable: true } } 复制代码 该方法的引入目的,主要是为了解决 Object.assign()无法正确拷贝 get 属性和 set 属性的问题。 Object.getOwnPropertyDescriptors()方法的另一个用处,是配合 Object.create()方法,将对象属性克隆到一个新对象。这属于浅拷贝。 const shallowClone = (obj) => Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj), ); 复制代码 更多详细内容参考ES 入门教程-getOwnPropertyDescriptors 函数参数的尾逗号 ES2017 允许函数的最后一个参数有尾逗号(trailing comma)。 此前,函数定义和调用时,都不允许最后一个参数后面出现逗号。 function clownsEverywhere(param1, param2,) { /* ... */ }

clownsEverywhere("foo", "bar",); 复制代码 更多详细内容参考ES 入门教程-函数参数的尾逗号 异步函数(Async functions) ES2017 标准引入了 async 函数,使得异步操作变得更加方便。 async 函数是什么?一句话,它就是 Generator 函数的语法糖。 function fakeRequest() { return new Promise((resolve, reject) => { setTimeout(() => { resolve("请求成功"); }, 2000); }); }

async function getData() { console.log("start"); const res = await fakeRequest(); console.log(res); console.log("end"); } getData(); /* 1.start 2.请求成功 3.end */ 复制代码 使用 Atomics 共享内存 Atomics 对象提供了一组静态方法对 SharedArrayBuffer 和 ArrayBuffer 对象进行原子操作。 更多详细内容参考MDN-Atomics

ES2016-ES7 Array.prototype.includes Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。 [1, 2, 3] .includes(2) // true [(1, 2, 3)].includes(4) // false [(1, 2, NaN)].includes(NaN); // true 复制代码 求幂运算符(Exponentiation operator) // 2的平方 2 ** 2; // 4 // 2的三次方 2 ** 3; // 8 复制代码 更多详细内容参考ES 入门教程-指数运算符

ES2015-ES6 推荐阮一峰大佬的ES 入门教程,中文文档没有比他更详细的了 箭头函数(arrows) 箭头函数是使用=>语法的函数简写。与一般函数不同的是

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

this 对象的指向是可变的,但是在箭头函数中,它是固定的。

不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

var f = (v) => v;

// 等同于 var f = function (v) { return v; };

function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); }

var id = 21; // 箭头函数导致this总是指向函数定义生效时所在的对象({id: 42}),所以打印出来的是42 foo.call({ id: 42 }); // id: 42

// 对象不构成单独的作用域,使得this指向全局对象 globalThis.s = 21; const obj = { s: 42, m: () => console.log(this.s), };

obj.m(); // 21 复制代码 更多详细内容参考ES 入门教程-箭头函数 类(Class) // ES5 function Point(x, y) { this.x = x; this.y = y; }

Point.prototype.toString = function () { return "(" + this.x + ", " + this.y + ")"; };

var p = new Point(1, 2);

// ES6 class Point { constructor(x, y) { this.x = x; this.y = y; }

toString() { return "(" + this.x + ", " + this.y + ")"; } } 复制代码 更多详细内容参考ES 入门教程-Class 对象的扩展(enhanced object literals) 对象的属性的简洁表示法 const foo = "bar"; const method = function () { return "Hello!"; };

const filed = "name";

const baz = { foo, method, [filed]: "小王", };

// 等同于 const baz = { foo: foo, method: function () { return "Hello!"; }, name: "小王", }; 复制代码 更多详细内容参考ES 入门教程-对象扩展 模板字符串 // 字符串中嵌入变量 let name = "Bob", time = "today"; Hello ${name}, how are you ${time}?; 复制代码 更多详细内容参考ES 入门教程-字符串模板 数组解构+扩展运算符 var [a] = [];

a === undefined; // true

var [a = 1] = []; a === 1; // true 复制代码 更多详细内容参考ES 入门教程-数组的扩展运算符 函数默认参数+剩余参数+扩展运算符 //如果没有传递y 或者y===undefined ,则y=12 function f(x, y = 12) { return x + y; } f(3) == 15; 复制代码 function f(x, ...y) { // y 是一个数组 return x * y.length; } f(3, "hello", true) == 6; 复制代码 function f(x, y, z) { return x + y + z; } // Pass each elem of array as argument f(...[1, 2, 3]) == 6; 复制代码 更多详细内容参考ES 入门教程-函数默认参数 块级作用域变量 随着 ES6 中引入 let/const 关键字,JS 才具有函数作用域和全局作用域,现在 JS 也可以有块级作用域了。 function f() { { let x; { // 正常,因为在一个新的块级作用域中 const x = "sneaky"; // const 定义的是常量无法被修改,因此会报错 x = "foo"; } // 在块级作用域中已声明x,因此会报错 let x = "inner"; } } 复制代码 更多详细内容参考ES 入门教程-unicode 遍历/迭代器+for..of(iterators + for..of) 一个数据结构只要部署了 Symbol.iterator 属性,就被视为具有 iterator 接口,就可以用 for...of 循环遍历它的成员。也就是说,for...of 循环内部调用的是数据结构的 Symbol.iterator 方法。 for ... of是for ... in和forEach()的替代方法,它循环访问可迭代的数据结构,如数组,映射,集合和字符串。 JavaScript 原有的 for...in 循环,只能获得对象的键名,不能直接获取键值。ES6 提供 for...of 循环,允许遍历获得键值。 var arr = ["a", "b", "c", "d"];

for (let a in arr) { console.log(a); // 0 1 2 3 }

for (let a of arr) { console.log(a); // a b c d }

const str = "helloworld"; for (let a of str) { console.log(a); // h e l l o w o r l d } 复制代码 更多详细内容参考ES 入门教程-iterators 生成器(generators) Generators 使用function *和yield简化了迭代器的创建。 声明为function 的函数一个遍历器对象,也就是说,Generator 函数是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。 生成器是迭代器的子类型,因此具有next和throw方法。 yield表达式是暂停执行的标记,而next方法可以恢复执行 注意:ES7 出现后,推荐使用await。 function foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; }

for (let v of foo()) { console.log(v); } // 1 2 3 4 5 复制代码 下面是一个利用 Generator 函数和for...of循环,实现斐波那契数列的例子。 var fibonacci = { [Symbol.iterator]: function* () { let [prev, curr] = [0, 1]; for (;😉 { yield curr; [prev, curr] = [curr, prev + curr]; } }, };

for (var n of fibonacci) { // if (n > 1000) break; console.log(n); } 复制代码 从上面代码可见,使用for...of语句时不需要使用next方法。 利用for...of循环,可以写出遍历任意对象(object)的方法。原生的 JavaScript 对象没有迭代器接口,无法使用for...of循环,通过 Generator 函数为它加上这个接口,就可以用了。 生成器(Generator) 实质上继承了迭代器(Iterator) interface Generator extends Iterator { next(value?: any): IteratorResult; throw(exception: any); } 复制代码 更多详细内容参考ES 入门教程-iterators Unicode ES6 增强了 Unicode 的功能,包括

支持字符的 Unicode 表示法

举例来说,“中”的 Unicode 码点是 U+4e2d,你可以直接在字符串里面输入这个汉字,也可以输入它的转义形式\u4e2d,两者是等价的。 "中" === "\u4e2d"; // true 复制代码

使用/u匹配码点的正则表达式

// new RegExp behaviour, opt-in ‘u’ "𠮷".match(/./u)[0].length == 2; 复制代码

获取 32 位的 UTF-16 字符的码点-codePointAt

"𠮷".codePointAt(0) == 0x20bb7;

let s = "𠮷a"; for (let ch of s) { console.log(ch.codePointAt(0).toString(16)); } // 20bb7 // 61 复制代码 更多详细内容参考ES 入门教程-unicode 模块化(modules) ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。 使用 export default 或 export 进行导出 // math.js export const pi = 3.141593;

export default function sum(x, y) { return x + y; } 复制代码 使用 import 进行导入 // app.js import sum, { pi } from "./math";

alert("2π = " + sum(pi, pi)); 复制代码 更多详细内容参考ES 入门教程-module 模块加载器规则(module loaders) 模块加载器支持:

异步加载 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。 模块之中,顶层的 this 关键字返回 undefined,而不是指向 window。也就是说,在模块顶层使用 this 关键字,是无意义的

//index.js const x = 1;

console.log(x === window.x); //false console.log(this === undefined); // true 复制代码 利用顶层的 this 等于 undefined 这个语法点,可以侦测当前代码是否在 ES6 模块之中。 const isNotModuleScript = this !== undefined; 复制代码 更多详细内容参考ES 入门教程-module-loader import and export Map + Set + Weakmap + Weakset ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 // Sets var s = new Set(); s.add("hello").add("goodbye").add("hello"); s.size === 2; s.has("hello") === true; 复制代码 ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。 // Maps var m = new Map(); m.set("hello", 42); m.set(s, 34); m.get(s) == 34; 复制代码 WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。 WeakMap 与 Map 的区别有两点。

WeakMap 只接受对象作为键名(null 除外),不接受其他类型的值作为键名。 WeakMap 的键名所指向的对象,不计入垃圾回收机制。

// Weak Maps var wm = new WeakMap(); wm.set(s, { extra: 42 }); wm.size === undefined; 复制代码 WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

WeakSet 的成员只能是对象,而不能是其他类型的值。 WeakSet 中的对象都是弱引用

// Weak Sets var ws = new WeakSet(); ws.add({ data: 42 }); // Because the added object has no other references, it will not be held in the set 复制代码 更多详细内容参考ES 入门教程-Set 和 Map 代理(proxies) Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改。 可以用于操作拦截,日志记录/分析等。 // 代理一个普通对象 var target = {}; var handler = { get: function (receiver, name) { return Hello, ${name}!; }, };

var p = new Proxy(target, handler);

// true p.world === "Hello, world!"; 复制代码 下面是 Proxy 所有可以代理的"元操作" var handler = { get:..., set:..., has:..., deleteProperty:..., apply:..., construct:..., getOwnPropertyDescriptor:..., defineProperty:..., getPrototypeOf:..., setPrototypeOf:..., enumerate:..., ownKeys:..., preventExtensions:..., isExtensible:... } 复制代码 MDN-handler.get() // 代理一个函数对象 var target = function () { return "I am the target"; }; var handler = { apply: function (receiver, ...args) { return "I am the proxy"; }, };

var p = new Proxy(target, handler); //true p() === "I am the proxy"; 复制代码 更多详细内容参考ES 入门教程-proxy symbols ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值 Symbol 值通过 Symbol 函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。 var MyClass = (function () { // var key = Symbol("key");

function MyClass(privateData) { this[key] = privateData; }

MyClass.prototype = { doStuff: function () { this[key]; }, };

return MyClass; })();

var c = new MyClass("hello"); // true console.log(c["key"] === undefined); 复制代码 创建 Symbol 的时候,可以添加一个描述。 const sym = Symbol("foo"); 复制代码 上面代码中,sym 的描述就是字符串 foo。 Symbol 作为属性名,遍历对象的时候,该属性不会出现在 for...in、for...of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。 但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。 const obj = {}; let a = Symbol("a"); let b = Symbol("b");

obj[a] = "Hello"; obj[b] = "World";

const objectSymbols = Object.getOwnPropertySymbols(obj);

objectSymbols; // [Symbol(a), Symbol(b)] 复制代码 更多详细内容参考ES 入门教程-symbol 期约(promises) Promise 是一个用于异步编程的库,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。 许多现有的 JavaScript 库已经使用了 Promise。 function timeout(duration = 0) { return new Promise((resolve, reject) => { setTimeout(resolve, duration); }); }

var p = timeout(1000) .then(() => { return timeout(2000); }) .then(() => { throw new Error("hmm"); }) .catch((err) => { return Promise.all([timeout(100), timeout(200)]); }); 复制代码 更多详细内容参考ES 入门教程-promise math + number + string + array + object APIs 添加了许多类型的扩展方法,包括:Math ,Array ,String ,Object Number.EPSILON; Number.isInteger(Infinity); // false Number.isNaN("NaN"); // false

Math.acosh(3); // 1.762747174039086 Math.hypot(3, 4); // 5 Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2); // 2

"abcde".includes("cd"); // true "abc".repeat(3); // "abcabcabc"

Array.from(document.querySelectorAll("*")); // Returns a real Array Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior [(0, 0, 0)].fill(7, 1) // [0,7,7] [(1, 2, 3)].find((x) => x == 3) // 3 [(1, 2, 3)].findIndex((x) => x == 2) // 1 [(1, 2, 3, 4, 5)].copyWithin(3, 0) // [1, 2, 3, 1, 2] [("a", "b", "c")].entries() // iterator [0, "a"], [1,"b"], [2,"c"] [("a", "b", "c")].keys() // iterator 0, 1, 2 [("a", "b", "c")].values(); // iterator "a", "b", "c"

Object.assign(Point, { origin: new Point(0, 0) }); 复制代码 更多详细内容参考 ES 入门教程:

Number Math, Array.from Array.of Array.prototype.copyWithin Object.assign

二进制和八进制(binary and octal literals) 两种新的数字表示形式。

二进制: 0b 开头 八进制: 0o 开头

0b111110111 === 503; // true 0o767 === 503; // true 复制代码 reflect api reflect API 公开对象上的运行时级别的元操作。 最重要的目的是配合 Proxy 使用,执行原生行为 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。 // 老写法 "assign" in Object; // true

// 新写法 Reflect.has(Object, "assign"); // true 复制代码 更多详细内容参考ES 入门教程-reflect 尾调用(tail calls)

尾调用:某个函数的最后一步是返回并调用另一个函数 尾递归:函数调用自身,称为递归。如果尾调用自身,就称为尾递归。 尾调用优化

注意,目前只有 Safari 浏览器支持尾调用优化,Chrome 和 Firefox 都不支持。这里就不深入研究了 😁 function factorial(n, acc = 1) { if (n <= 1) return acc; return factorial(n - 1, n * acc); }

// 大多数浏览器中都会出现 堆栈溢出 的错误, // 但是在 ES6的Safari中是安全的 factorial(100000); 复制代码 更多详细内容参考ES 入门教程-尾调用 通过 Intl API 对字符串,数字和日期进行国际化 Intl 对象是 ECMAScript 国际化 API 的命名空间,它提供对语言敏感的字符串比较、支持数字格式化以及日期和时间的格式化。 Intl.Collator 对象 collator 这个单词意思是排序器。Intl.Collator 对象是排序器的构造函数,可以支持对语言敏感的字符串比较。

中文排序

如果我们希望我们的中文按照首字母拼音排序,该怎么处理? 此时,可以使用中文简体的 BCF 47 语言标记字符串 zh 进行排序,代码如下: var arrUsername = [ "陈坤", "邓超", "杜淳", "冯绍峰", "韩庚", "胡歌", "黄晓明", "贾乃亮", "李晨", "李易峰", "鹿晗", "井柏然", "刘烨", "陆毅", "孙红雷", ];

arrUsername.sort(new Intl.Collator("zh").compare); // 结果是:["陈坤", "邓超", "杜淳", "冯绍峰", "韩庚", "胡歌", "黄晓明", "贾乃亮", "井柏然", "李晨", "李易峰", "刘烨", "陆毅", "鹿晗", "孙红雷"] 复制代码 Intl API详细可以参考这篇文章JS Intl 对象完整简介及在中文中的应用

ES2011-ES5 相信大家已经对 ES5 都了然于胸,因此只做简单罗列,就不举例说明了 'USE STRICT' JS 的早期版本允许使用未声明的变量。 但是当使用 es5 "use strict"功能时,会报告错误 // index.js "use strict";

// 报错:a is not defined a = 22; 复制代码 Array Array.isArray Array.forEach Array.map Array.filter Array.reduce Array.reduceRight Array.every Array.some Array.indexOf Array.lastIndexOf JSON JSON.parse JSON.stringify DATE Date.now() Date.now().valueOf() Object.defineProperty()

参考文档

ECMAScript 6 Features es6-features.org ES2021 Features with simple examples 4 个强大 JavaScript 运算符 ES6 核心特性

上次更新: 8/30/2021, 3:26:12 PM