ES2019 的所有新技巧和窍门

Avatar of Laurie Barth
Laurie Barth

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

ECMAScript 标准再次更新,在 ES2019 中添加了新功能。现在 已在 Node、Chrome、Firefox 和 Safari 中正式可用,您还可以使用 Babel 将这些功能编译成 JavaScript 的不同版本,如果您需要支持旧版浏览器。

让我们看看有什么新功能吧!

Object.fromEntries

在 ES2017 中,我们引入了 Object.entries。这是一个将对象转换为其数组表示形式的函数。类似这样

let students = {
  amelia: 20,
  beatrice: 22,
  cece: 20,
  deirdre: 19,
  eloise: 21
}

Object.entries(students) 
// [
//  [ 'amelia', 20 ],
//  [ 'beatrice', 22 ],
//  [ 'cece', 20 ],
//  [ 'deirdre', 19 ],
//  [ 'eloise', 21 ]
// ]

这是一个很棒的补充,因为它允许对象利用 Array 原型中内置的众多函数。例如 mapfilterreduce 等。不幸的是,它需要一个稍微手动的过程才能将结果转换回对象。

let students = {
  amelia: 20,
  beatrice: 22,
  cece: 20,
  deirdre: 19,
  eloise: 21
}

// convert to array in order to make use of .filter() function
let overTwentyOne = Object.entries(students).filter(([name, age]) => {
  return age >= 21
}) // [ [ 'beatrice', 22 ], [ 'eloise', 21 ] ]

// turn multidimensional array back into an object
let DrinkingAgeStudents = {}
for (let [name, age] of overTwentyOne) {
    DrinkingAgeStudents[name] = age;
}
// { beatrice: 22, eloise: 21 }

Object.fromEntries 旨在消除此循环!它为您提供了更简洁的代码,邀请您在对象上使用数组原型方法。

let students = {
  amelia: 20,
  beatrice: 22,
  cece: 20,
  deirdre: 19,
  eloise: 21
}

// convert to array in order to make use of .filter() function
let overTwentyOne = Object.entries(students).filter(([name, age]) => {
  return age >= 21
}) // [ [ 'beatrice', 22 ], [ 'eloise', 21 ] ]

// turn multidimensional array back into an object
let DrinkingAgeStudents = Object.fromEntries(overTwentyOne); 
// { beatrice: 22, eloise: 21 }

需要注意的是,数组和对象是不同的数据结构,这是有原因的。在某些情况下,在两者之间切换会导致数据丢失。下面数组元素变为重复对象键的示例就是其中之一。

let students = [
  [ 'amelia', 22 ], 
  [ 'beatrice', 22 ], 
  [ 'eloise', 21], 
  [ 'beatrice', 20 ]
]

let studentObj = Object.fromEntries(students); 
// { amelia: 22, beatrice: 20, eloise: 21 }
// dropped first beatrice!

使用这些函数时,请务必注意潜在的副作用。

Object.fromEntries 支持情况

Chrome Firefox Safari Edge
75 67 12.1 不支持

🔍 我们需要您的帮助。 您是否可以访问移动浏览器以测试这些和其他功能?请在评论中留下您的结果——我们会查看并将其包含在文章中。

Array.prototype.flat

多维数组是一种非常常见的数据结构,尤其是在检索数据时。扁平化它的能力是必要的。它一直都是可能的,但并不美观。

让我们以以下示例为例,其中我们的映射为我们留下了一个我们想要扁平化的多维数组。

let courses = [
  {
    subject: "math",
    numberOfStudents: 3,
    waitlistStudents: 2,
    students: ['Janet', 'Martha', 'Bob', ['Phil', 'Candace']]
  },
  {
    subject: "english",
    numberOfStudents: 2,
    students: ['Wilson', 'Taylor']
  },
  {
    subject: "history",
    numberOfStudents: 4,
    students: ['Edith', 'Jacob', 'Peter', 'Betty']
  }
]

let courseStudents = courses.map(course => course.students)
// [
//   [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
//   [ 'Wilson', 'Taylor' ],
//   [ 'Edith', 'Jacob', 'Peter', 'Betty' ]
// ]

[].concat.apply([], courseStudents) // we're stuck doing something like this

Array.prototype.flat 出现了。它接受一个可选的深度参数。

let courseStudents = [
  [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
  [ 'Wilson', 'Taylor' ],
  [ 'Edith', 'Jacob', 'Peter', 'Betty' ]
]

let flattenOneLevel = courseStudents.flat(1)
console.log(flattenOneLevel)
// [
//   'Janet',
//   'Martha',
//   'Bob',
//   [ 'Phil', 'Candace' ],
//   'Wilson',
//   'Taylor',
//   'Edith',
//   'Jacob',
//   'Peter',
//   'Betty'
// ]

let flattenTwoLevels = courseStudents.flat(2)
console.log(flattenTwoLevels)
// [
//   'Janet',   'Martha',
//   'Bob',     'Phil',
//   'Candace', 'Wilson',
//   'Taylor',  'Edith',
//   'Jacob',   'Peter',
//   'Betty'
// ]

请注意,如果未提供参数,则默认深度为 1。这一点非常重要,因为在我们的示例中,这不会完全扁平化数组。

let courseStudents = [
  [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
  [ 'Wilson', 'Taylor' ],
  [ 'Edith', 'Jacob', 'Peter', 'Betty' ]
]

let defaultFlattened = courseStudents.flat()
console.log(defaultFlattened)
// [
//   'Janet',
//   'Martha',
//   'Bob',
//   [ 'Phil', 'Candace' ],
//   'Wilson',
//   'Taylor',
//   'Edith',
//   'Jacob',
//   'Peter',
//   'Betty'
// ]

做出此决定的理由是,该函数默认情况下不是贪婪的,并且需要明确的指令才能按此方式操作。对于具有完全扁平化数组意图的未知深度,可以使用 Infinity 参数。

let courseStudents = [
  [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ],
  [ 'Wilson', 'Taylor' ],
  [ 'Edith', 'Jacob', 'Peter', 'Betty' ]
]

let alwaysFlattened = courseStudents.flat(Infinity)
console.log(alwaysFlattened)
// [
//   'Janet',   'Martha',
//   'Bob',     'Phil',
//   'Candace', 'Wilson',
//   'Taylor',  'Edith',
//   'Jacob',   'Peter',
//   'Betty'
// ]

与往常一样,应谨慎使用贪婪操作,如果数组的深度确实未知,则它们可能不是一个好的选择。

Array.prototype.flat 支持情况

Chrome Firefox Safari Edge
75 67 12 不支持
Chrome Android Firefox Android iOS Safari IE Mobile Samsung Internet Android Webview
75 67 12.1 不支持 不支持 67

Array.prototype.flatMap

随着 flat 的添加,我们也得到了 Array.prototype.flatMap 的组合函数。我们实际上已经在上面看到了一个这个函数有用处的例子,但让我们再看一个。

如果我们想在数组中插入元素会怎样?在 ES2019 添加之前,它会是什么样子?

let grades = [78, 62, 80, 64]

let curved = grades.map(grade => [grade, grade + 7])
// [ [ 78, 85 ], [ 62, 69 ], [ 80, 87 ], [ 64, 71 ] ]

let flatMapped = [].concat.apply([], curved) // now flatten, could use flat but that didn't exist before either
// [
//  78, 85, 62, 69,
//  80, 87, 64, 71
// ]

现在我们有了 Array.prototype.flat,我们可以稍微改进这个例子。

let grades = [78, 62, 80, 64]

let flatMapped = grades.map(grade => [grade, grade + 7]).flat()
// [
//  78, 85, 62, 69,
//  80, 87, 64, 71
// ]

但是,这仍然是一种比较流行的模式,尤其是在函数式编程中。因此,将其内置到数组原型中非常棒。使用 flatMap,我们可以这样做

let grades = [78, 62, 80, 64]

let flatMapped = grades.flatMap(grade => [grade, grade + 7]);
// [
//  78, 85, 62, 69,
//  80, 87, 64, 71
// ]

现在,请记住 Array.prototype.flat 的默认参数是 1。而 flatMap 等同于组合 mapflat 且不带参数。因此,flatMap 只会扁平化一层。

let grades = [78, 62, 80, 64]

let flatMapped = grades.flatMap(grade => [grade, [grade + 7]]);
// [
//   78, [ 85 ],
//   62, [ 69 ],
//   80, [ 87 ],
//   64, [ 71 ]
// ]

Array.prototype.flatMap 支持情况

Chrome Firefox Safari Edge
75 67 12 不支持
Chrome Android Firefox Android iOS Safari IE Mobile Samsung Internet Android Webview
75 67 12.1 不支持 不支持 67

String.trimStart 和 String.trimEnd

ES2019 中的另一个不错的补充是别名,它使某些字符串函数名称更明确。以前,String.trimRightString.trimLeft 是可用的。

let message = "   Welcome to CS 101    "
message.trimRight()
// '   Welcome to CS 101'
message.trimLeft()
// 'Welcome to CS 101   '
message.trimRight().trimLeft()
// 'Welcome to CS 101'

这些都是很棒的函数,但是为它们提供更符合其目的的名称也很有益。删除开头空格和结尾空格。

let message = "   Welcome to CS 101    "
message.trimEnd()
// '   Welcome to CS 101'
message.trimStart()
// 'Welcome to CS 101   '
message.trimEnd().trimStart()
// 'Welcome to CS 101'

String.trimStart 和 String.trimEnd 支持情况

Chrome Firefox Safari Edge
75 67 12 不支持

可选捕获绑定

ES2019 中的另一个不错的功能是使 try-catch 块中的参数可选。以前,所有 catch 块都将异常作为参数传入。这意味着即使 catch 块中的代码忽略了它,它也仍然存在。

try {
  let parsed = JSON.parse(obj)
} catch(e) {
  // ignore e, or use
  console.log(obj)
}

现在情况不再如此。如果 catch 块中未使用异常,则根本不需要传入任何内容。

try {
  let parsed = JSON.parse(obj)
} catch {
  console.log(obj)
}

如果您已经知道错误是什么并且正在查找触发它的数据,这是一个很好的选择。

可选捕获绑定支持情况

Chrome Firefox Safari Edge
75 67 12 不支持

Function.toString() 更改

ES2019 还对 Function.toString() 的工作方式进行了更改。以前,它完全删除了空格。

function greeting() {
  const name = 'CSS Tricks'
  console.log(`hello from ${name}`)
}

greeting.toString()
//'function greeting() {\nconst name = \'CSS Tricks\'\nconsole.log(`hello from ${name} //`)\n}'

现在它反映了函数在源代码中的真实表示形式。

function greeting() {
  const name = 'CSS Tricks'
  console.log(`hello from ${name}`)
}

greeting.toString()
// 'function greeting() {\n' +
//  "  const name = 'CSS Tricks'\n" +
//  '  console.log(`hello from ${name}`)\n' +
//  '}'

这主要是一个内部更改,但我禁不住认为这可能也会在将来让一两个博主的生活更轻松。

Function.toString 支持情况

Chrome Firefox Safari Edge
75 60 12 – 部分支持 17 – 部分支持

就是这样!ES2019 的主要功能新增。

还有一些其他新增功能您可能需要探索。其中包括

快乐的 JavaScript 编程!