ECMAScript 中的新特性
ES 2021
String.prototype.replaceAll
替换字符串中所有匹配项。
const newStr = str.replaceAll(regexp | substr, newSubstr)
regexp|substr
匹配的正则表达式(必须为全局模式,即/abc/g
)或者字符串。newSubstr
用于替换的字符串。返回值
替换后的字符串。
'123456123'.replaceAll('123', '000') === '000456000'
'abcdab'.replaceAll(/ab/g, '000') === '000cd000'
'abcdab'.replaceAll(/ef/g, '000') === 'abcdab'
在搜索的字符串为空的情况下,replace
和 replaceAll
的区别如下。
'x'.replace('', '_')
// → '_x'
'xxx'.replace(/(?:)/g, '_')
// → '_x_x_x_'
'xxx'.replaceAll('', '_')
// → '_x_x_x_'
参考资料:
Promise.any
和 AggregateError
当传入的所有 Promise 中有一个成功时,就返回那个成功的 Promise,然后终止操作,不会等待其他 Promise。如果所有 Promise 都被拒绝,则会返回 AggregateError
。
Promise.any(iterable)
iterable
可迭代对象,比如 Array。
try {
const first = await Promise.any(promises)
// Any of the promises was fulfilled.
} catch (error) {
// All of the promises were rejected.
}
Or, without async
/await
:
Promise.any(promises).then(
first => {
// Any of the promises was fulfilled.
},
error => {
// All of the promises were rejected.
}
)
下面有一个例子。
Promise.any([
fetch('https://v8.dev/').then(() => 'home'),
fetch('https://v8.dev/blog').then(() => 'blog'),
fetch('https://v8.dev/docs').then(() => 'docs'),
])
.then(first => {
// Any of the promises was fulfilled.
console.log(first)
// → 'home'
})
.catch(error => {
// All of the promises were rejected.
console.log(error)
})
参考资料:
逻辑赋值符号 ||=
&&=
??=
把逻辑运算符和赋值表达式结合起来。
// "Or Or Equals" (or, the Mallet operator :wink:)
a ||= b
a || (a = b)
// "And And Equals"
a &&= b
a && (a = b)
// "QQ Equals"
a ??= b
a ?? (a = b)
参考资料:tc39/proposal-logical-assignment: A proposal to combine Logical Operators and Assignment Expressions
数字分隔符 _
就是英文里用于分隔很长的数字的 ,
在这里以下划线 _
的形式出现。
1_000_000_000 // Ah, so a billion
101_475_938.38 // And this is hundreds of millions
let fee = 123_00 // $123 (12300 cents, apparently)
let fee = 12_300 // $12,300 (woah, that fee!)
let amount = 12345_00 // 12,345 (1234500 cents, apparently)
let amount = 123_4500 // 123.45 (4-fixed financial)
let amount = 1_234_500 // 1,234,500
0.000_001 // 1 millionth
1e10_000 // 10^10000 -- granted, far less useful / in-range...
更多用法可以看提案里的 Examples。
参考资料:tc39/proposal-numeric-separator: A proposal to add numeric literal separators in JavaScript.
WeakRefs
用于保留对一个对象的弱引用,不会阻止 GC(JavaScript 引擎的垃圾回收机制)。
下面的例子创建了一个计时器,计时器元素不存在时停止。
class Counter {
constructor(element) {
// 记录一个弱引用到 DOM 元素
this.ref = new WeakRef(element)
this.start()
}
start() {
if (this.timer) {
return
}
this.count = 0
const tick = () => {
// 从弱引用中获取元素,如果存在就执行操作
const element = this.ref.deref()
if (element) {
element.textContent = ++this.count
} else {
// 元素不存在时
console.log('The element is gone.')
this.stop()
this.ref = null
}
}
tick()
this.timer = setInterval(tick, 1000)
}
stop() {
if (this.timer) {
clearInterval(this.timer)
this.timer = 0
}
}
}
const counter = new Counter(document.getElementById('counter'))
counter.start()
setTimeout(() => {
document.getElementById('counter').remove()
}, 5000)
参考资料:
ES 2020
String.prototype.matchAll
匹配正则表达式,并返回结果的迭代器。
str.matchAll(regexp)
regexp
要匹配的正则表达式,必须为g
全局模式。返回值
一个可迭代对象,不可重用,可以使用[...data]
语法转换成数组。
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"]
参考资料:
BigInt
在数字后面加字母 n
表示大整数。
ECMAScript 中 Number 类型的最大值为 Number.MAX_SAFE_INTEGER === 2^53 - 1
,即 9007199254740991,而 BigInt 可以表示任意大的整数。
typeof 1n === 'bigint' // true
const theBiggestInt = 9007199254740991n
const alsoHuge = BigInt(9007199254740991)
// ↪ 9007199254740991n
const hugeButString = BigInt('9007199254740991')
// ↪ 9007199254740991n
BigInt 可以使用 +
*
-
**
和 %
符号,但符号两边必须都是 BigInt 类型,否则会报错。
const previousMaxSafe = BigInt(Number.MAX_SAFE_INTEGER);
// ↪ 9007199254740991
const maxPlusOne = previousMaxSafe + 1n;
// ↪ 9007199254740992n
const theFuture = previousMaxSafe + 2n;
// ↪ 9007199254740993n, this works now!
const multi = previousMaxSafe * 2n;
// ↪ 18014398509481982n
const subtr = multi – 10n;
// ↪ 18014398509481972n
const mod = multi % 10n;
// ↪ 2n
const bigN = 2n ** 54n;
// ↪ 18014398509481984n
bigN * -1n
// ↪ –18014398509481984n
1n + 2
// ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions
1n * 2
// ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions
除号 /
会向零取整。
const expected = 4n / 2n
// ↪ 2n
const rounded = 5n / 2n
// ↪ 2n, not 2.5n
BigInt 在比较的时候不严格等于 Number,两者可以比较大小,也可以混合排序。
0n === 0 // ↪ false
0n == 0 // ↪ true
1n < 2 // ↪ true
2n > 1 // ↪ true
2 > 2 // ↪ false
2n > 2 // ↪ false
2n >= 2 // ↪ true
const mixed = [4n, 6, -12n, 10, 4, 0, 0n]
// ↪ [4n, 6, -12n, 10, 4, 0, 0n]
mixed.sort()
// ↪ [-12n, 0, 0n, 10, 4n, 4, 6]
BigInt 在转换成 Boolean 的情况下的行为类似 Number。
if (0n) {
console.log('Hello from the if!')
} else {
console.log('Hello from the else!')
}
// ↪ "Hello from the else!"
0n || 12n // ↪ 12n
0n && 12n // ↪ 0n
Boolean(0n) // ↪ false
Boolean(12n) // ↪ true
!12n // ↪ false
!0n // ↪ true
BigInt 需要使用 Number()
手动转换类型到 Number,不能使用隐形类型转换。
;+1n
// ↪ TypeError: Cannot convert a BigInt value to a number
Number(1n)
// ↪ 1
不过字符串没这个限制。
1n + '2'
// ↪ "12"
'2' + 1n
// ↪ "21"
试图把 BigInt 直接转换成 Number 可能会造成精度丢失。
const largeFriend = 900719925474099267n
const alsoLarge = largeFriend + 2n
const sendMeTheBiggest = (n, m) => Math.max(Number(n), Number(m))
sendMeTheBiggest(largeFriend, alsoLarge)
// ↪900719925474099300 // This is neither argument!
Number(151851850485185185047n)
// ↪ 151851850485185200000
parseInt(900719925474099267n)
// ↪900719925474099300
可以使用 BigInt(number|string)
来生成一个大整数,不过同样需要注意精度的问题,传入的如果是 Number,则要小心不要超出范围,建议直接传入字符串作为参数或者直接数字后面加 n
。
const badPrecision = BigInt(9007199254740993)
// ↪9007199254740992n
const goodPrecision = BigInt('9007199254740993')
// ↪9007199254740993n
const alsoGoodPrecision = 9007199254740993n
// ↪9007199254740993n
BigInt()
的参数不能是小数,字符串表示的小数也不行。
BigInt(1.5)
// ↪ RangeError: The number 1.5 is not a safe integer and thus cannot be converted to a BigInt
BigInt('1.5')
// ↪ SyntaxError: Cannot convert 1.5 to a BigInt
BigInt 不能使用 Math
中的方法。
Math.round(1n)
// ↪ TypeError: Cannot convert a BigInt value to a number
Math.max(1n, 10n)
// ↪ TypeError: Cannot convert a BigInt value to a number
1n | 0
// ↪ TypeError: Cannot mix BigInt and other types, use explicit conversions
还有一点需要注意的是,JSON.stringify
不能序列化 BigInt。
const bigObj = { a: BigInt(10n) }
JSON.stringify(bigObj)
// ↪TypeError: Do not know how to serialize a BigInt
参考资料:
for-in
遍历对象中的可枚举属性。
var obj = { a: 1, b: 2, c: 3 }
for (var prop in obj) {
console.log('obj.' + prop + ' = ' + obj[prop])
}
// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"
空值合并运算符 ??
当左侧为 undefined
或者 null
时,返回右侧值。
主要用来解决 ||
操作符将 ''
0
false
判断为假的情况,??
会把以上这些判断为真,并返回右侧的值。
const response = {
settings: {
nullValue: null,
height: 400,
animationDuration: 0,
headerText: '',
showSplashScreen: false,
},
}
const undefinedValue = response.settings.undefinedValue ?? 'some other default'
// result: 'some other default'
const nullValue = response.settings.nullValue ?? 'some other default'
// result: 'some other default'
const headerText = response.settings.headerText ?? 'Hello, world!'
// result: ''
const animationDuration = response.settings.animationDuration ?? 300
// result: 0
const showSplashScreen = response.settings.showSplashScreen ?? true
// result: false
需要注意的是,??
在没有明确优先级的情况下,不能与 &&
和 ||
并列使用。
null || undefined ?? "foo"; // 抛出 SyntaxError
true || undefined ?? "foo"; // 抛出 SyntaxError
(null || undefined ) ?? "foo"; // 返回 "foo"
参考资料:
可选链操作符 ?.
如果操作符左侧为 undefined
或者 null
则表达式为 undefined
,否则正常执行操作符右侧的属性、方法和函数调用。
obj?.prop // 调用可选的静态属性
obj?.[expr] // 调用可选的动态属性
func?.(...args) // 调用可选的函数或者方法
arr?.[index] // 调用可能的数组项
这其实是一种简化的写法,请看下面的例子。
a?.b // 如果 a 是 null/undefined 则返回 undefined 否则返回 a.b
a == null ? undefined : a.b
a?.[x] // 如果 a 是 null/undefined 则返回 undefined 否则返回 a[x]
a == null ? undefined : a[x]
a?.b() // 如果 a 是 null/undefined 则返回 undefined
a == null ? undefined : a.b()
// 如果 a.b 不是函数,则抛出 TypeError
// 否则执行 a.b()
a?.() // 如果 a 是 null/undefined 则返回 undefined
a == null ? undefined : a()
// 如果 a 不是 null/undefined 也不是函数,则抛出 TypeError
// 否则调用函数 a
更多用例可以查看提案中的 Semantics 部分以及 MDN 中的 例子 。
参考资料:
Promise.allSettled
所有 Promise 都完成时(无论是 fulfilled
还是 reject
),返回结果的数组。
const promises = [
fetch('https://lifeni.life'),
fetch('https://does-not-exist/'),
]
const results = await Promise.allSettled(promises)
const errors = results.filter(p => p.status === 'rejected').map(p => p.reason)
console.log(JSON.stringify(results))
/** output
[
{
"status":"fulfilled",
"value":{...}
},
{
"status":"rejected",
"reason":{...}
}
]
*/
参考资料:
import()
也就是动态导入,返回一个 Promise。
<!DOCTYPE html>
<nav>
<a href="books.html" data-entry-module="books">Books</a>
<a href="movies.html" data-entry-module="movies">Movies</a>
<a href="video-games.html" data-entry-module="video-games">Video Games</a>
</nav>
<main>Content will load here!</main>
<script>
const main = document.querySelector('main')
for (const link of document.querySelectorAll('nav > a')) {
link.addEventListener('click', e => {
e.preventDefault()
import(`./section-modules/${link.dataset.entryModule}.js`)
.then(module => {
module.loadPageInto(main)
})
.catch(err => {
main.textContent = err.message
})
})
}
</script>
参考资料:
import.meta
获取模块的元数据。
<script type="module" src="my-module.mjs"></script>
console.log(import.meta) // { url: "file:///home/user/my-module.mjs" }
下面是一个例子。
<script
type="module"
src="path/to/hamster-displayer.mjs"
data-size="500"
></script>
;(async () => {
// new URL() 的第二个参数是相对地址(baseUrl)
const response = await fetch(new URL('../hamsters.jpg', import.meta.url))
const blob = await response.blob()
const size = import.meta.scriptElement.dataset.size || 300
const image = new Image()
image.src = URL.createObjectURL(blob)
image.width = image.height = size
document.body.appendChild(image)
})()
参考资料:
globalThis
在不同的运行环境下,获取全局对象的方式不同。
在以前,从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中,可以通过
window
、self
或者frames
取到全局对象,但是在 Web Workers 中,只有self
可以。在 Node.js 中,它们都无法获取,必须使用global
。
var getGlobal = function () {
if (typeof self !== 'undefined') {
return self
}
if (typeof window !== 'undefined') {
return window
}
if (typeof global !== 'undefined') {
return global
}
throw new Error('unable to locate global object')
}
var globals = getGlobal()
if (typeof globals.setTimeout !== 'function') {
// 此环境中没有 setTimeout 方法!
}
因此为了简化代码,可以使用 globalThis
来调用全局对象。
if (typeof globalThis.setTimeout !== 'function') {
// 此环境中没有 setTimeout 方法!
}
参考资料:
ES 2019
Object.fromEntries
相当于 Object.entries
的反向操作,生成一个对象。
obj = Object.fromEntries([
['a', 0],
['b', 1],
]) // { a: 0, b: 1 }
可以将 Map 或者 Array 转化为 Object。
const map = new Map([
['foo', 'bar'],
['baz', 42],
])
const obj = Object.fromEntries(map)
console.log(obj) // { foo: "bar", baz: 42 }
const arr = [
['0', 'a'],
['1', 'b'],
['2', 'c'],
]
const obj = Object.fromEntries(arr)
console.log(obj) // { 0: "a", 1: "b", 2: "c" }
如果键或者值不是字符串,则可能会出现意料之外的结果。
const map = new Map([
[{}, 'a'],
[{}, 'b'],
])
Object.fromEntries(map)
// → { '[object Object]': 'b' }
// Note: the value 'a' is nowhere to be found, since both keys
// stringify to the same value of '[object Object]'.
参考资料:
String.prototype.{trimStart,trimEnd}
删除字符串中 开头/结尾 的连续空白符,也可以使用别名 String.prototype.{trimLeft,trimRight}
。
var str = ' foo '
console.log(str.length) // 8
str = str.trimStart() // 等同于 str = str.trimLeft();
console.log(str.length) // 5
console.log(str) // "foo "
str = str.trimRight() // 或写成str = str.trimEnd();
console.log(str.length) // 6
console.log(str) // ' foo'
参考资料:
Array.prototype.{flat,flatMap}
Array.prototype.flat
用来把一个嵌套的数组展开成一个一维的数组。
var newArray = arr.flat([depth])
depth
展开的深度,默认为 1。
var arr1 = [1, 2, [3, 4]]
arr1.flat()
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]]
arr2.flat()
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]]
arr3.flat(2)
// [1, 2, 3, 4, 5, 6]
// flat 会去除空元素
var arr4 = [1, 2, , 4, 5]
arr4.flat()
// [1, 2, 4, 5]
Array.prototype.flatMap
相当于把 map()
的结果进行 flat()
,展开深度为 1。
var arr1 = [1, 2, 3, 4]
arr1.map(x => [x * 2])
// [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2])
// [2, 4, 6, 8]
// 只会展开一层
arr1.flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]
参考资料:
Function.prototype.toString
把函数的源码转换成字符串,建议直接看 MDN 上的 示例,很详细。
Symbol.prototype.description
返回 Symbol
的描述。
Symbol('desc').toString() // "Symbol(desc)"
Symbol('desc').description // "desc"
Symbol('').description // ""
Symbol().description // undefined
// well-known symbols
Symbol.iterator.toString() // "Symbol(Symbol.iterator)"
Symbol.iterator.description // "Symbol.iterator"
// global symbols
Symbol.for('foo').toString() // "Symbol(foo)"
Symbol.for('foo').description // "foo"
可选的 catch
绑定
如果不需要 catch
中的 Error 绑定,则可以省略。
// 原来的写法,不需要 Error,但仍然要绑定变量到 Error
try {
// 尝试使用可能不被支持的 Web 特性
} catch (unused) {
// 退回到被广泛支持的 Web 特性
}
// 现在的写法
try {
// ...
} catch {
// ...
}
参考资料:tc39/proposal-optional-catch-binding: proposal for ECMAScript to allow omission of the catch binding
将 ECMAScript 语法拓展为 JSON 的超集
由于 JSON 中的字符串可以包含非转义的 U+2028 行分隔符 和 U+2029 段分隔符,而 ECMAScript 2018 及之前的字符串中不包含,所以在使用 JSON.parse
时会出现 SyntaxError
。
// A raw U+2029 character, produced by eval:
const PS = eval('"\u2029"')
// ES 2018: SyntaxError
// ES 2019: ok!
参考资料:
格式正确的 JSON.stringify
JSON 规定使用 UTF-8 进行编码,但是对于一些编码,JSON.stringify
可能会出现解析失败的问题(这个地方看文档不太明白,啥 UTF-16 什么的)。所以提案对这种情况采取转义 Unicode 的方式。
// Non-BMP characters still serialize to surrogate pairs.
JSON.stringify('𝌆')
// → '"𝌆"'
JSON.stringify('\uD834\uDF06')
// → '"𝌆"'
// Unpaired surrogate code units will serialize to escape sequences.
JSON.stringify('\uDF06\uD834')
// → '"\\udf06\\ud834"'
JSON.stringify('\uDEAD')
// → '"\\udead"'
参考资料:
ES 2018
异步迭代
这里只介绍 for-await-of
的用法,其他比如 generator
等内容可以查看下面的参考资料。
// 创建一个可迭代的异步对象
var asyncIterable = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return Promise.resolve({ value: this.i++, done: false })
}
return Promise.resolve({ done: true })
},
}
},
}
// 然后可以使用 for await of 遍历这个对象
;(async function () {
for await (num of asyncIterable) {
console.log(num)
}
})()
// 0
// 1
// 2
参考资料:
对象属性的 Spread
和 Rest
语法
把 ES 6 里的 ...
语法拓展到了对象的属性上。
// 把剩余的属性赋值给 z
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }
x // 1
y // 2
z // { a: 3, b: 4 }
// 展开 z 中的属性
let n = { x, y, ...z }
n // { x: 1, y: 2, a: 3, b: 4 }
参考资料:
Promise.prototype.finally
无论 Promise 的结果如何,都会执行回调函数。
let isLoading = true
fetch(myRequest)
.then(function (response) {
var contentType = response.headers.get('content-type')
if (contentType && contentType.includes('application/json')) {
return response.json()
}
throw new TypeError("Oops, we haven't got JSON!")
})
.then(function (json) {
/* process your JSON further */
})
.catch(function (error) {
console.log(error)
})
.finally(function () {
isLoading = false
})
参考资料:
模板字符串的修订
带标签的模版字符串应该允许嵌套支持常见转义序列的语言(例如 DSLs、LaTeX)。ECMAScript 提议模版字面量修订(第 4 阶段,将要集成到 ECMAScript 2018 标准)移除对 ECMAScript 在带标签的模版字符串中转义序列的语法限制。
function latex(str) {
return { cooked: str[0], raw: str.raw[0] }
}
latex`\unicode`
// 非法转义序列变成了 undefined
// { cooked: undefined, raw: "\\unicode" }
// 不带标签的没事
let bad = `bad escape sequence: \unicode`
正则标识 s
在正则表达式后添加标识 s
可以使得 .
匹配任意字符,包含行终止符 \n
,用来代替 [^]
这个写法。
// ES 2017 -
;/foo.bar/.test('foo\nbar')
// → false
;/foo[^]bar/.test('foo\nbar')
// → true
// ES 2018 +
;/foo.bar/s.test('foo\nbar')
// → true
正则反向断言
肯定的反向断言使用 (?<=...)
的格式。
const reLookbehind = /(?<=\$)\d+(\.\d*)?/
const match1 = reLookbehind.exec('$123.89')
const match2 = reLookbehind.exec('€123.89')
console.log(match1[0]) // 123.89
console.log(match2) // null
否定的反向断言使用 (?<!...)
的格式。
const reLookbehind = /(?<!\$)\d+(?:\.\d*)/
const match1 = reLookbehind.exec('$10.53')
const match2 = reLookbehind.exec('€10.53')
console.log(match1[0]) // 0.53
console.log(match2[0]) // 10.53
参考资料:tc39/proposal-regexp-lookbehind: RegExp lookbehind assertions
正则命名组
在正则表达式中使用 ?<name>
的形式可以给一个匹配组进行命名。
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
let result = re.exec('2015-01-02')
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';
// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';
也可以使用解构简化代码。
let {
groups: { one, two },
} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar')
console.log(`one: ${one}, two: ${two}`) // prints one: foo, two: bar
参考资料:tc39/proposal-regexp-named-groups: Named capture groups for JavaScript RegExps
正则 Unicode 转义
在正则表达式中使用 \p{…}
和 \P{…}
的格式来转义 Unicode。
// GreekSymbol 是希腊符号的意思
const regexGreekSymbol = /\p{Script=Greek}/u
regexGreekSymbol.test('π')
// → true
ES 2017
异步函数
使用 async
定义一个返回 Promise 的函数,函数内可以使用 await
。
async function name([param[, param[, ... param]]]) {
statements
}
更多资料可以去 Google 的 文档 看看。
参考资料:
Object.{values,entries}
Object.values
返回一个对象所有可枚举的 属性值 的数组。Object.entries
返回一个对象所有可枚举的 键值对 组成的数组。
var obj = { foo: 'bar', baz: 42 }
Object.values(obj) // ['bar', 42]
Object.entries(obj) // [ ['foo', 'bar'], ['baz', 42] ]
// 类似数组的对象
var obj = { 0: 'a', 1: 'b', 2: 'c' }
Object.values(obj) // ['a', 'b', 'c']
Object.entries(obj) // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
// 乱序的类似数组的对象
// 当使用数字作为键时,值将会按照键的数字顺序返回
var an_obj = { 100: 'a', 2: 'b', 7: 'c' }
Object.values(an_obj) // ['b', 'c', 'a']
Object.entries(an_obj) // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]
// getFoo 是一个不可枚举的属性
var my_obj = Object.create(
{},
{
getFoo: {
value: function () {
return this.foo
},
},
}
)
my_obj.foo = 'bar'
Object.values(my_obj) // ['bar']
Object.entries(my_obj) // [ ['foo', 'bar']
// 非对象的参数将会强制转换成对象
Object.values('foo') // ['f', 'o', 'o']
Object.entries('foo') // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]
Object.entries
还有很多用法。
// 优雅地遍历 key-value
const obj = { a: 5, b: 7, c: 9 }
for (const [key, value] of Object.entries(obj)) {
console.log(`${key} ${value}`) // "a 5", "b 7", "c 9"
}
// 或者使用数组遍历对象
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} ${value}`) // "a 5", "b 7", "c 9"
})
// 将 Object 转换成 Map
var obj = { foo: 'bar', baz: 42 }
var map = new Map(Object.entries(obj))
console.log(map) // Map { foo: "bar", baz: 42 }
参考资料:
String.prototype.{padStart,padEnd}
用于 从头/从尾 部填充字符串到指定长度。
str.padStart(targetLength [, padString])
targetLength
要填充到的长度,如果小于当前字符串的长度,则返回当前字符串。padString
要填充的字符串,超出的部分会被截断。默认填充空格
'abc'.padStart(10) // " abc"
'abc'.padStart(10, 'foo') // "foofoofabc"
'abc'.padStart(6, '123465') // "123abc"
'abc'.padStart(8, '0') // "00000abc"
'abc'.padStart(1) // "abc"
String.prototype.padEnd
同理。
'abc'.padEnd(10) // "abc "
'abc'.padEnd(10, 'foo') // "abcfoofoof"
'abc'.padEnd(6, '123456') // "abc123"
'abc'.padEnd(1) // "abc"
参考资料:
允许在函数参数中添加末尾逗号
如果函数有很多参数,那么经过格式化之后,参数会垂直排列。如果要添加一个参数,那么在代码管理软件(例如 Git)看来,实际上修改了两行:上一个参数后添加逗号、新的参数。
function clownPuppiesEverywhere(
param1,
param2, // updated to add a comma
param3 // updated to add new parameter
) {
/* ... */
}
clownPuppiesEverywhere(
'foo',
'bar', // updated to add a comma
'baz' // updated to add new parameter
)
但是如果允许默认情况下在最后一个参数后加上逗号,那么未来添加新的参数时,要修改的代码就只有一行。
function clownPuppiesEverywhere(
param1,
param2 // 添加下一个参数时只会修改下面这一行,而不会修改这一行
) {
/* ... */
}
clownPuppiesEverywhere(
'foo',
'bar' // 添加下一个参数时只会修改下面这一行,而不会修改这一行
)
// 这样也是可以的
obj(1, 2, 3)
其实不止函数的参数可以这样做,数组、对象的末尾逗号也是可以的,并且在 ECMAScript 5 中就已经得到了支持。但是在 JSON 中是不行的。
参考资料:
Object.getOwnPropertyDescriptors
返回指定对象的所有自身属性的描述符。
Object.getOwnPropertyDescriptors(Date)
/** output
UTC: {writable: true, enumerable: false, configurable: true, value: ƒ}
length: {value: 7, writable: false, enumerable: false, configurable: true}
name: {value: "Date", writable: false, enumerable: false, configurable: true}
now: {writable: true, enumerable: false, configurable: true, value: ƒ}
parse: {writable: true, enumerable: false, configurable: true, value: ƒ}
prototype: {value: {…}, writable: false, enumerable: false, configurable: false}
__proto__: Object
*/
可以用来进行浅拷贝。
Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))
参考资料:
共享内存和 Atomics
这里的共享内存指的是 SharedArrayBuffer
对象,用于 Web Worker 和主线程之间内存的共享。
关于 SharedArrayBuffer
和 Atomics
的更多信息可以查看下面的参考资料。
参考资料:
ES 2016
Array.prototype.includes
用于判断数组中是否包含某个值。
arr.includes(value[, fromIndex])
value
要查找的值。fromIndex
开始查找的位置,默认为 0。返回值
Boolean。
;[1, 2, 3].includes(2) === true
;[1, 2, 3].includes(4) === false
;[1, 2, NaN].includes(NaN) === true
;[1, 2, -0].includes(+0) === true
;[1, 2, +0].includes(-0) === true
;['a', 'b', 'c'].includes('a') === true
;['a', 'b', 'c'].includes('a', 1) === false
String.prototype.includes
与这个方法类似。
参考资料:
指数运算符 x ** y
表示 x 的 y 次方,相当于 Math.pow(x, y)
。
// x ** y
let cubed = 2 ** 3
// 相当于 2 * 2 * 2
// x **= y
let a = 2
a **= 2
// 相当于 a = a * a;
参考资料:tc39/proposal-exponentiation-operator: Progress tracking for ES7 exponentiation operator
其他参考资料
- 所有进入标准的提案汇总:proposals/finished-proposals.md at master · tc39/proposals
- MDN 上的文档:JavaScript 参考 - JavaScript | MDN
- ECMAScript 2021 标准:ECMAScript® 2021 Language Specification