记录干杯

​( ゜- ゜)つロ 干杯 ~
开往

ECMAScript 中的新特性

ES 2021

String.prototype.replaceAll

替换字符串中所有匹配项。

const newStr = str.replaceAll(regexp | substr, newSubstr)
'123456123'.replaceAll('123', '000') === '000456000'
'abcdab'.replaceAll(/ab/g, '000') === '000cd000'
'abcdab'.replaceAll(/ef/g, '000') === 'abcdab'

在搜索的字符串为空的情况下,replacereplaceAll 的区别如下。

'x'.replace('', '_')
// → '_x'
'xxx'.replace(/(?:)/g, '_')
// → '_x_x_x_'
'xxx'.replaceAll('', '_')
// → '_x_x_x_'

参考资料:

Promise.anyAggregateError

当传入的所有 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)
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"

参考资料:for…in - JavaScript | MDN

空值合并运算符 ??

当左侧为 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 中,可以通过 windowself 或者 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 上的 示例,很详细。

参考资料:Function.prototype.toString() - JavaScript | 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"

参考资料:Symbol.prototype.description - JavaScript | MDN

可选的 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

参考资料:

对象属性的 SpreadRest 语法

把 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
  })

参考资料:

模板字符串的修订

带标签的模版字符串应该允许嵌套支持常见转义序列的语言(例如 DSLsLaTeX)。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`

参考资料:模板字符串 - JavaScript | MDN

正则标识 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

参考资料:tc39/proposal-regexp-dotall-flag: Proposal to add the s (dotAll) flag to regular expressions in ECMAScript.

正则反向断言

肯定的反向断言使用 (?<=...) 的格式。

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

参考资料:tc39/proposal-regexp-unicode-property-escapes: Proposal to add Unicode property escapes \p{…} and \P{…} to regular expressions in ECMAScript.

ES 2017

异步函数

使用 async 定义一个返回 Promise 的函数,函数内可以使用 await

async function name([param[, param[, ... param]]]) {
   statements
}

更多资料可以去 Google 的 文档 看看。

参考资料:

Object.{values,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])
'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 和主线程之间内存的共享。

关于 SharedArrayBufferAtomics 的更多信息可以查看下面的参考资料。

参考资料:

ES 2016

Array.prototype.includes

用于判断数组中是否包含某个值。

arr.includes(value[, fromIndex])
;[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

其他参考资料

网站正在重新设计中,部分功能和内容还没完成。