了解 JavaScript 中的重构、其余参数和展期语法

作者选择了 COVID-19 救援基金作为 Write for Donations计划的一部分接受捐款。

介绍

自 ECMAScript 规范的 2015 版发布以来,许多用于 arraysobjects的工作的新功能都已在 JavaScript语言中提供。你将在本文中学习的一些值得注意的功能是 destructuringrest parametersspread 语法。

许多其他语言没有用于破坏、休息参数和扩散的相应语法,因此这些功能可能对新的JavaScript开发人员和来自其他语言的开发者都有学习曲线。

破坏

Destructuring assignment 是一种语法,允许您将对象属性分配为变量,从而大大减少在这些结构中操纵数据所需的代码行。

对象破坏

对象破构允许您使用一个对象属性作为值创建新的 变量

考虑下面的例子,一个代表一个有id标题日期的笔记的对象:

1const note = {
2  id: 1,
3  title: 'My first note',
4  date: '01/01/1970',
5}

传统上,如果您想为每个属性创建一个新的变量,则需要单独分配每个变量,并重复很多次:

1// Create variables from the Object properties
2const id = note.id
3const title = note.title
4const date = note.date

通过将每个变量围绕在弯曲的子中,JavaScript将从每个具有相同名称的属性中创建新的变量:

1// Destructure properties into variables
2const { id, title, date } = note

现在, console.log()新的变量:

1console.log(id)
2console.log(title)
3console.log(date)

您将获得原始资产值作为输出:

1[secondary_label Output]
21
3My first note
401/01/1970

<$>[注] **注:**破坏一个对象不会改变原始对象。

对象破构的默认分配会创建与对象属性相同名称的新变量. 如果您不希望新变量与属性名称相同的名称,您还可以通过使用列(:)重命名新变量来决定新名称,如下文中的 `noteId 所示:

1// Assign a custom name to a destructured value
2const { id: noteId, title, date } = note

将新变量noteId登录到控制台:

1console.log(noteId)

您将获得以下输出:

1[secondary_label Output]
21

您还可以破坏嵌入式对象值,例如,更新注释对象,以便嵌入式作者对象:

1const note = {
2  id: 1,
3  title: 'My first note',
4  date: '01/01/1970',
5  author: {
6    firstName: 'Sherlock',
7    lastName: 'Holmes',
8  },
9}

现在你可以破坏笔记,然后再次破坏以从作者属性中创建变量:

1// Destructure nested properties
2const {
3  id,
4  title,
5  date,
6  author: { firstName, lastName },
7} = note

接下来,使用 template literals登录新的变量 firstNamelastName:

1console.log(`${firstName} ${lastName}`)

这将产生以下产出:

1[secondary_label Output]
2Sherlock Holmes

请注意,在本示例中,虽然您可以访问作者对象的内容,但作者对象本身无法访问。

1// Access object and nested values
2const {
3  author,
4  author: { firstName, lastName },
5} = note
6
7console.log(author)

此代码将输出作者对象:

1[secondary_label Output]
2{firstName: "Sherlock", lastName: "Holmes"}

破坏一个对象不仅有助于减少你必须编写的代码量;它还允许你针对你关心的属性。

最后,可以使用破构来访问原始值的对象属性,例如,String是字符串的全球对象,并且具有一个长度属性:

1const { length } = 'A string'

这将找到一个字符串的固有长度属性,并将其设置为等于长度变量。

1console.log(length)

你会得到以下的输出:

1[secondary_label Output]
28

字符串一个字符串在这里被默认地转换为对象,以获取长度属性。

雷达破坏

Array 破构允许您使用一个 Array 项目作为值创建新的变量. 考虑这个例子,一个具有日期的不同部分的数组:

1const date = ['1970', '12', '01']

JavaScript 中的数组保证保留其顺序,所以在这种情况下,第一个索引总是是一年,第二个将是月,等等.知道这一点,你可以从数组中的项目创建变量:

1// Create variables from the Array items
2const year = date[0]
3const month = date[1]
4const day = date[2]

但是手动做这件事可能会在代码中占用大量空间. 通过数组破坏,您可以按顺序解包数组中的值,并将它们分配给自己的变量,如下:

1// Destructure Array values into variables
2const [year, month, day] = date

现在记录新的变量:

1console.log(year)
2console.log(month)
3console.log(day)

你会得到以下的输出:

1[secondary_label Output]
21970
312
401

可以通过将破坏性语法空置于字符串之间跳过值:

1// Skip the second item in the array
2const [year, , day] = date
3
4console.log(year)
5console.log(day)

运行此操作将给的值:

1[secondary_label Output]
21970
301

嵌入式数组也可以被破坏,首先,创建一个嵌入式数组:

1// Create a nested array
2const nestedArray = [1, 2, [3, 4], 5]

然后摧毁该数组并记录新的变量:

1// Destructure nested items
2const [one, two, [three, four], five] = nestedArray
3
4console.log(one, two, three, four, five)

您将获得以下输出:

1[secondary_label Output]
21 2 3 4 5

破坏语法可以应用于破坏函数中的参数. 要测试这一点,您将从 Object.entries()中破坏

首先,声明笔记对象:

1const note = {
2  id: 1,
3  title: 'My first note',
4  date: '01/01/1970',
5}

鉴于此对象,您可以通过将参数转移到 [forEach() 方法](https://andsky.com/tech/tutorials/how-to-use-array-methods-in-javascript-iteration-methods#foreach() 来列出关键值对:

1// Using forEach
2Object.entries(note).forEach(([key, value]) => {
3  console.log(`${key}: ${value}`)
4})

或者你可以使用相同的 for loop:

1// Using a for loop
2for (let [key, value] of Object.entries(note)) {
3  console.log(`${key}: ${value}`)
4}

无论如何,你都会得到以下内容:

1[secondary_label Output]
2id: 1
3title: My first note
4date: 01/01/1970

对象破坏和数组破坏可以结合到一个单一的破坏分配中。 默认参数也可以用于破坏,正如本示例所示,该示例将默认日期设置为 new Date()

首先,声明笔记对象:

1const note = {
2  title: 'My first note',
3  author: {
4    firstName: 'Sherlock',
5    lastName: 'Holmes',
6  },
7  tags: ['personal', 'writing', 'investigations'],
8}

然后摧毁对象,同时设置一个新的日期变量,默认为新日期():

1const {
2  title,
3  date = new Date(),
4  author: { firstName },
5  tags: [personalTag, writingTag],
6} = note
7
8console.log(date)

’console.log(date)’ 将产生类似于以下的输出:

1[secondary_label Output]
2Fri May 08 2020 23:53:49 GMT-0500 (Central Daylight Time)

正如本节所示,破坏分配语法为JavaScript增加了大量的灵活性,并允许您编写更简要的代码。

散布

_Spread_语法(...)是JavaScript的另一个有用的补充,用于处理数组、对象和函数调用。Spread允许对象和可迭代(如数组)被解包或扩展,可以用来制作数据结构的浅副本,以增加数据操纵的方便性。

通过 Arrays 传播

扩散可以简化使用数组的常见任务,例如,假设您有两个数组,并希望将它们结合起来:

1// Create an Array
2const tools = ['hammer', 'screwdriver']
3const otherTools = ['wrench', 'saw']

起初,你会使用 concat()来连接两个数组:

1// Concatenate tools and otherTools together
2const allTools = tools.concat(otherTools)

现在,您还可以使用 Spread 将数组解包到一个新的数组中:

1// Unpack the tools Array into the allTools Array
2const allTools = [...tools, ...otherTools]
3
4console.log(allTools)

运行这将带来如下:

1[secondary_label Output]
2["hammer", "screwdriver", "wrench", "saw"]

例如,您可能正在使用一个具有用户存储在一系列对象的应用程序:

1// Array of users
2const users = [
3  { id: 1, name: 'Ben' },
4  { id: 2, name: 'Leslie' },
5]

您可以使用来修改现有数组并添加一个新用户,这将是可变的选项:

1// A new user to be added
2const newUser = { id: 3, name: 'Ron' }
3
4users.push(newUser)

但这改变了用户的范围,我们可能想保留。

Spread 允许您从现有数组中创建一个新数组,并在末尾添加一个新项目:

1const updatedUsers = [...users, newUser]
2
3console.log(users)
4console.log(updatedUsers)

现在新数组更新用户有新用户,但原来的用户数组保持不变:

1[secondary_label Output]
2[{id: 1, name: "Ben"}
3 {id: 2, name: "Leslie"}]
4
5[{id: 1, name: "Ben"}
6 {id: 2, name: "Leslie"}
7 {id: 3, name: "Ron"}]

在 JavaScript 中,当您创建一个对象或数组并将其分配到另一个变量时,您实际上不是创建一个新对象,而是传递一个参考。

举一个例子,其中一个数组被创建并分配给另一个变量:

1// Create an Array
2const originalArray = ['one', 'two', 'three']
3
4// Assign Array to another variable
5const secondArray = originalArray

删除第二个数组的最后一个项目将更改第一个:

1// Remove the last item of the second Array
2secondArray.pop()
3
4console.log(originalArray)

这将带来产量:

1[secondary_label Output]
2["one", "two"]

扩散允许您创建一个数组或对象的浅副本,这意味着任何顶级属性都将被克隆,但嵌入的对象仍将通过参考。

如果您编写相同的示例代码,但复制扩散的数组,原始数组将不再被更改:

 1// Create an Array
 2const originalArray = ['one', 'two', 'three']
 3
 4// Use spread to make a shallow copy
 5const secondArray = [...originalArray]
 6
 7// Remove the last item of the second Array
 8secondArray.pop()
 9
10console.log(originalArray)

以下内容将登录到控制台:

1[secondary_label Output]
2["one", "two", "three"]

它也可以用來轉換一個 設定,或任何其他 iterable到一個數字。

创建一个新集,并添加一些条目:

1// Create a set
2const set = new Set()
3
4set.add('octopus')
5set.add('starfish')
6set.add('whale')

接下来,使用设置的扩散运算器并记录结果:

1// Convert Set to Array
2const seaCreatures = [...set]
3
4console.log(seaCreatures)

这将给出如下:

1[secondary_label Output]
2["octopus", "starfish", "whale"]

这也可以用来从字符串创建一个数组:

1const string = 'hello'
2
3const stringArray = [...string]
4
5console.log(stringArray)

这将给出一个数组,每个字符作为数组中的一个项目:

1[secondary_label Output]
2["h", "e", "l", "l", "o"]

通过物体传播

在使用对象时,可以使用 Spread 来复制和更新对象。

最初, Object.assign()用于复制一个对象:

1// Create an Object and a copied Object with Object.assign()
2const originalObject = { enabled: true, darkMode: false }
3const secondObject = Object.assign({}, originalObject)

第二个对象现在将是原来的对象的克隆。

通过扩散语法来简化这一点,您可以通过将其扩散到一个新的对象来轻松复制一个对象:

1// Create an object and a copied object with spread
2const originalObject = { enabled: true, darkMode: false }
3const secondObject = { ...originalObject }
4
5console.log(secondObject)

这将导致如下:

1[secondary_label Output]
2{enabled: true, darkMode: false}

与数组一样,这只会创建一个浅层的副本,而嵌入式对象仍然会通过参考。

以不变的方式添加或修改现有对象的属性是通过扩散来简化的. 在本示例中,对用户对象添加了isLoggedIn属性:

1const user = {
2  id: 3,
3  name: 'Ron',
4}
5
6const updatedUser = { ...user, isLoggedIn: true }
7
8console.log(updatedUser)

这将产生如下:

1[secondary_label Output]
2{id: 3, name: "Ron", isLoggedIn: true}

通过扩散更新对象的一个重要事项是,任何嵌入式对象也必须被扩散,例如,假设在用户对象中有一个嵌入式组织对象:

1const user = {
2  id: 3,
3  name: 'Ron',
4  organization: {
5    name: 'Parks & Recreation',
6    city: 'Pawnee',
7  },
8}

如果您尝试在组织中添加一个新项目,则将重写现有字段:

1const updatedUser = { ...user, organization: { position: 'Director' } }
2
3console.log(updatedUser)

这将导致如下:

1[secondary_label Output]
2id: 3
3name: "Ron"
4organization: {position: "Director"}

如果可变性不是一个问题,则可以直接更新该字段:

1user.organization.position = 'Director'

但是,由于我们正在寻找一个不变的解决方案,我们可以扩展内部对象以保持现有的属性:

1const updatedUser = {
2  ...user,
3  organization: {
4    ...user.organization,
5    position: 'Director',
6  },
7}
8
9console.log(updatedUser)

这将给出如下:

1[secondary_label Output]
2id: 3
3name: "Ron"
4organization: {name: "Parks & Recreation", city: "Pawnee", position: "Director"}

通过函数呼叫传播

Spread 也可以用于函数调用中的参数。

举个例子,这里有一个倍增函数,它采取三个参数并将它们倍增:

1// Create a function to multiply three items
2function multiply(a, b, c) {
3  return a * b * c
4}

通常情况下,您会将三个值单独作为参数传递给函数调用,如下:

1multiply(1, 2, 3)

这将给出如下:

1[secondary_label Output]
26

但是,如果您要传输到函数的所有值已经存在于一个数组中,则扩散语法允许您将数组中的每个项目用作参数:

1const numbers = [1, 2, 3]
2
3multiply(...numbers)

这将产生相同的结果:

1[secondary_label Output]
26

<$>[注] **注:**没有扩散,可以通过使用 apply() 实现:

1multiply.apply(null, [1, 2, 3])

这将给出:

1[secondary_label Output]
26

美元

现在你已经看到了扩散如何缩短你的代码,你可以看看......语法的另一种使用:休息参数。

其他参数

您将在本文中学习的最后一个特征是 rest parameter 语法. 语法与 spread (...) 相同,但具有相反的效果. 而不是将一个数组或对象解包成单个值,其余的语法将创建一个无限数量的参数的数组。

例如,在restTest函数中,如果我们希望args是一个由无限数量的参数组成的数组,我们可以有如下:

1function restTest(...args) {
2  console.log(args)
3}
4
5restTest(1, 2, 3, 4, 5, 6)

所有转移到restTest函数的参数现在可在args数组中使用:

1[secondary_label Output]
2[1, 2, 3, 4, 5, 6]

休息语法可以用作唯一的参数或列表中的最后一个参数. 如果用作唯一的参数,它会收集所有参数,但如果它位于列表的末尾,则会收集剩余的每个参数,如本示例所示:

1function restTest(one, two, ...args) {
2  console.log(one)
3  console.log(two)
4  console.log(args)
5}
6
7restTest(1, 2, 3, 4, 5, 6)

这将单独考虑前两个参数,然后将其余的参数组成一个数组:

1[secondary_label Output]
21
32
4[3, 4, 5, 6]

在较旧的代码中,可以使用参数变量来汇集到函数中传递的所有参数:

1function testArguments() {
2  console.log(arguments)
3}
4
5testArguments('how', 'many', 'arguments')

这将产生以下产出:

1[secondary_label Output]1
2Arguments(3) ["how", "many", "arguments"]

然而,这有几个缺点:首先,参数变量不能与箭头函数一起使用。

1const testArguments = () => {
2  console.log(arguments)
3}
4
5testArguments('how', 'many', 'arguments')

这会导致一个错误:

1[secondary_label Output]
2Uncaught ReferenceError: arguments is not defined

此外,论点不是一个真实的数组,不能使用像地图过滤器这样的方法(https://andsky.com/tech/tutorials/how-to-use-array-methods-in-javascript-iteration-methods#map(),而不会首先转换为一个数组。

休息也可以用于破坏阵列时:

1const [firstTool, ...rest] = ['hammer', 'screwdriver', 'wrench']
2
3console.log(firstTool)
4console.log(rest)

这将给出:

1[secondary_label Output]
2hammer
3["screwdriver", "wrench"]

休息也可以用来破坏物体:

1const { isLoggedIn, ...rest } = { id: 1, name: 'Ben', isLoggedIn: true }
2
3console.log(isLoggedIn)
4console.log(rest)

提供以下产出:

1[secondary_label Output]
2true
3{id: 1, name: "Ben"}

这样,休息语法提供了有效的方法来收集无限数量的项目。

结论

在本文中,您了解了破坏、散布语法和休息参数。

  • 使用破构来创建从数组项目或对象属性的变量
  • Spread 语法用于解包可重复性,如数组、对象和函数调用
  • 休息参数语法将从无限数量的值中创建一个数组

破坏结构、休息参数和扩散语法是JavaScript中有用的功能,有助于保持代码简短和清洁。

如果你想看到结构化在行动中,请看看 如何自定义使用 Props 的反应组件,它使用这种语法来破坏数据,并将其传递给自定义前端组件。

Published At
Categories with 技术
comments powered by Disqus