了解JavaScript中的解构、Rest参数和传播语法
作者选择了 COVID-19 Relief Fund 作为 Write for DOnations 计划的一部分来接受捐赠。
介绍
自 ECMAScript 规范的 2015 版 以来,JavaScript 语言已经可以使用许多用于处理 arrays 和 objects 的新功能。 您将在本文中学习的一些值得注意的语法是 destructuring、rest parameters 和 spread 语法。 这些特性提供了更直接的方法来访问数组或对象的成员,并且可以使使用这些数据结构更快、更简洁。
许多其他语言没有相应的解构、剩余参数和展开语法,因此这些特性可能对新的 JavaScript 开发人员和来自其他语言的开发人员都有一个学习曲线。 在本文中,您将学习如何解构对象和数组,如何使用扩展运算符解包对象和数组,以及如何在函数调用中使用剩余参数。
解构
解构赋值 是一种语法,允许您将对象属性或数组项分配为变量。 这可以大大减少操作这些结构中的数据所需的代码行。 解构有两种类型:对象解构和数组解构。
对象解构
对象解构允许您使用对象属性作为值来创建新的 变量 。
考虑这个例子,一个表示带有 id
、title
和 date
音符的对象:
const note = { id: 1, title: 'My first note', date: '01/01/1970', }
传统上,如果您想为每个属性创建一个新变量,则必须单独分配每个变量,并进行大量重复:
// Create variables from the Object properties const id = note.id const title = note.title const date = note.date
使用对象解构,这一切都可以在一行中完成。 通过将每个变量括在大括号 {}
中,JavaScript 将从每个具有相同名称的属性创建新变量:
// Destructure properties into variables const { id, title, date } = note
现在,console.log() 新变量:
console.log(id) console.log(title) console.log(date)
您将获得原始属性值作为输出:
Output1 My first note 01/01/1970
注意: 解构对象不会修改原始对象。 您仍然可以调用原始的 note
并且其所有条目都完好无损。
对象解构的默认赋值会创建与对象属性同名的新变量。 如果您不希望新变量与属性名称同名,您还可以选择使用冒号 (:
) 重命名新变量来决定新名称,如 [ X198X] 如下:
// Assign a custom name to a destructured value const { id: noteId, title, date } = note
将新变量 noteId
记录到控制台:
console.log(noteId)
您将收到以下输出:
Output1
您还可以解构嵌套对象值。 例如,将 note
对象更新为具有嵌套的 author
对象:
const note = { id: 1, title: 'My first note', date: '01/01/1970', author: { firstName: 'Sherlock', lastName: 'Holmes', }, }
现在您可以解构 note
,然后再次解构以从 author
属性创建变量:
// Destructure nested properties const { id, title, date, author: { firstName, lastName }, } = note
接下来,使用 模板文字 记录新变量 firstName
和 lastName
:
console.log(`${firstName} ${lastName}`)
这将给出以下输出:
OutputSherlock Holmes
请注意,在此示例中,尽管您可以访问 author
对象的内容,但无法访问 author
对象本身。 为了访问对象及其嵌套值,您必须单独声明它们:
// Access object and nested values const { author, author: { firstName, lastName }, } = note console.log(author)
此代码将输出 author
对象:
Output{firstName: "Sherlock", lastName: "Holmes"}
解构对象不仅有助于减少您必须编写的代码量; 它还允许您定位对您关心的属性的访问。
最后,解构可用于访问原始值的对象属性。 例如,String 是字符串的全局对象,并具有 length
属性:
const { length } = 'A string'
这将找到字符串的固有长度属性并将其设置为等于 length
变量。 记录 length
以查看这是否有效:
console.log(length)
您将获得以下输出:
Output8
字符串 A string
在此处被隐式转换为对象以检索 length
属性。
数组解构
数组解构允许您使用数组项作为值来创建新变量。 考虑这个例子,一个包含日期各个部分的数组:
const date = ['1970', '12', '01']
JavaScript 中的数组保证保持它们的顺序,所以在这种情况下,第一个索引总是一年,第二个索引是月份,依此类推。 知道了这一点,您可以从数组中的项目创建变量:
// Create variables from the Array items const year = date[0] const month = date[1] const day = date[2]
但是手动执行此操作会占用代码中的大量空间。 使用数组解构,您可以按顺序从数组中解压缩值并将它们分配给它们自己的变量,如下所示:
// Destructure Array values into variables const [year, month, day] = date
现在记录新变量:
console.log(year) console.log(month) console.log(day)
您将获得以下输出:
Output1970 12 01
可以通过在逗号之间将解构语法留空来跳过值:
// Skip the second item in the array const [year, , day] = date console.log(year) console.log(day)
运行它会给出 year
和 day
的值:
Output1970 01
嵌套数组也可以被解构。 首先,创建一个嵌套数组:
// Create a nested array const nestedArray = [1, 2, [3, 4], 5]
然后解构该数组并记录新变量:
// Destructure nested items const [one, two, [three, four], five] = nestedArray console.log(one, two, three, four, five)
您将收到以下输出:
Output1 2 3 4 5
解构语法可用于解构函数中的参数。 为了测试这一点,您将从 Object.entries() 中解构 keys
和 values
。
首先,声明 note
对象:
const note = { id: 1, title: 'My first note', date: '01/01/1970', }
给定这个对象,您可以通过解构参数来列出键值对,因为它们被传递给 forEach() 方法 :
// Using forEach Object.entries(note).forEach(([key, value]) => { console.log(`${key}: ${value}`) })
或者你可以使用 for 循环 来完成同样的事情:
// Using a for loop for (let [key, value] of Object.entries(note)) { console.log(`${key}: ${value}`) }
无论哪种方式,您都会收到以下信息:
Outputid: 1 title: My first note date: 01/01/1970
对象解构和数组解构可以组合在一个解构赋值中。 默认参数也可以与解构一起使用,如本例所示,将默认日期设置为new Date()。
首先,声明 note
对象:
const note = { title: 'My first note', author: { firstName: 'Sherlock', lastName: 'Holmes', }, tags: ['personal', 'writing', 'investigations'], }
然后解构对象,同时设置一个新的 date
变量,默认值为 new Date()
:
const { title, date = new Date(), author: { firstName }, tags: [personalTag, writingTag], } = note console.log(date)
console.log(date)
然后将给出类似于以下的输出:
OutputFri May 08 2020 23:53:49 GMT-0500 (Central Daylight Time)
如本节所示,解构赋值语法为 JavaScript 增加了很多灵活性,并允许您编写更简洁的代码。 在下一节中,您将看到如何使用扩展语法将数据结构扩展为它们的组成数据条目。
传播
Spread 语法 (...
) 是 JavaScript 的另一个有用的补充,用于处理数组、对象和函数调用。 Spread 允许对对象和可迭代对象(如数组)进行解包或扩展,可用于制作数据结构的浅拷贝,以增加数据操作的便利性。
用数组传播
Spread 可以使用数组简化常见任务。 例如,假设您有两个数组并想要组合它们:
// Create an Array const tools = ['hammer', 'screwdriver'] const otherTools = ['wrench', 'saw']
最初您将使用 concat() 连接两个数组:
// Concatenate tools and otherTools together const allTools = tools.concat(otherTools)
现在您还可以使用 spread 将数组解包到一个新数组中:
// Unpack the tools Array into the allTools Array const allTools = [...tools, ...otherTools] console.log(allTools)
运行它会给出以下结果:
Output["hammer", "screwdriver", "wrench", "saw"]
这对于不变性特别有用。 例如,您可能正在使用将 users
存储在对象数组中的应用程序:
// Array of users const users = [ { id: 1, name: 'Ben' }, { id: 2, name: 'Leslie' }, ]
您可以使用 push
修改现有数组并添加新用户,这将是可变选项:
// A new user to be added const newUser = { id: 3, name: 'Ron' } users.push(newUser)
但这会改变我们可能想要保留的 user
数组。
Spread 允许您从现有数组创建一个新数组并在末尾添加一个新项:
const updatedUsers = [...users, newUser] console.log(users) console.log(updatedUsers)
现在新数组 updatedUsers
有了新用户,但原来的 users
数组保持不变:
Output[{id: 1, name: "Ben"} {id: 2, name: "Leslie"}] [{id: 1, name: "Ben"} {id: 2, name: "Leslie"} {id: 3, name: "Ron"}]
创建数据副本而不是更改现有数据有助于防止意外更改。 在 JavaScript 中,当您创建一个对象或数组并将其分配给另一个变量时,您实际上并不是在创建一个新对象,而是在传递一个引用。
以这个例子为例,其中创建了一个数组并将其分配给另一个变量:
// Create an Array const originalArray = ['one', 'two', 'three'] // Assign Array to another variable const secondArray = originalArray
删除第二个数组的最后一项将修改第一个:
// Remove the last item of the second Array secondArray.pop() console.log(originalArray)
这将给出输出:
Output["one", "two"]
Spread 允许您制作数组或对象的浅拷贝,这意味着任何顶级属性都将被克隆,但嵌套对象仍将通过引用传递。 对于简单的数组或对象,您可能只需要一个浅拷贝。
如果您编写相同的示例代码但复制带有 spread 的数组,则将不再修改原始数组:
// Create an Array const originalArray = ['one', 'two', 'three'] // Use spread to make a shallow copy const secondArray = [...originalArray] // Remove the last item of the second Array secondArray.pop() console.log(originalArray)
以下将记录到控制台:
Output["one", "two", "three"]
Spread 也可用于将 set 或任何其他 iterable 转换为数组。
创建一个新集并向其中添加一些条目:
// Create a set const set = new Set() set.add('octopus') set.add('starfish') set.add('whale')
接下来,将扩展运算符与 set
一起使用并记录结果:
// Convert Set to Array const seaCreatures = [...set] console.log(seaCreatures)
这将给出以下内容:
Output["octopus", "starfish", "whale"]
这对于从字符串创建数组也很有用:
const string = 'hello' const stringArray = [...string] console.log(stringArray)
这将给出一个数组,其中每个字符作为数组中的一项:
Output["h", "e", "l", "l", "o"]
用对象传播
处理对象时,spread 可用于复制和更新对象。
最初, Object.assign() 用于复制对象:
// Create an Object and a copied Object with Object.assign() const originalObject = { enabled: true, darkMode: false } const secondObject = Object.assign({}, originalObject)
secondObject
现在将是 originalObject
的克隆。
使用扩展语法可以简化这一点——您可以通过将对象扩展为新对象来浅拷贝对象:
// Create an object and a copied object with spread const originalObject = { enabled: true, darkMode: false } const secondObject = { ...originalObject } console.log(secondObject)
这将导致以下结果:
Output{enabled: true, darkMode: false}
就像数组一样,这只会创建一个浅拷贝,嵌套对象仍然会通过引用传递。
以不可变的方式在现有对象上添加或修改属性可以通过扩展简化。 在此示例中,isLoggedIn
属性被添加到 user
对象:
const user = { id: 3, name: 'Ron', } const updatedUser = { ...user, isLoggedIn: true } console.log(updatedUser)
这将输出以下内容:
Output{id: 3, name: "Ron", isLoggedIn: true}
通过传播更新对象需要注意的一件重要事情是,任何嵌套对象也必须被传播。 例如,假设在 user
对象中有一个嵌套的 organization
对象:
const user = { id: 3, name: 'Ron', organization: { name: 'Parks & Recreation', city: 'Pawnee', }, }
如果您尝试向 organization
添加新项目,它将覆盖现有字段:
const updatedUser = { ...user, organization: { position: 'Director' } } console.log(updatedUser)
这将导致以下结果:
Outputid: 3 name: "Ron" organization: {position: "Director"}
如果可变性不是问题,则可以直接更新该字段:
user.organization.position = 'Director'
但是由于我们正在寻求一个不可变的解决方案,我们可以传播内部对象以保留现有属性:
const updatedUser = { ...user, organization: { ...user.organization, position: 'Director', }, } console.log(updatedUser)
这将给出以下内容:
Outputid: 3 name: "Ron" organization: {name: "Parks & Recreation", city: "Pawnee", position: "Director"}
使用函数调用传播
Spread 也可以与函数调用中的参数一起使用。
例如,下面是一个 multiply
函数,它接受三个参数并将它们相乘:
// Create a function to multiply three items function multiply(a, b, c) { return a * b * c }
通常,您会将三个值分别作为参数传递给函数调用,如下所示:
multiply(1, 2, 3)
这将给出以下内容:
Output6
但是,如果您要传递给函数的所有值都已存在于数组中,则扩展语法允许您将数组中的每个项目用作参数:
const numbers = [1, 2, 3] multiply(...numbers)
这将给出相同的结果:
Output6
注:不展开,可以使用apply()
来完成:
multiply.apply(null, [1, 2, 3])
这将给出:
Output6
现在您已经了解了 spread 如何缩短您的代码,您可以看看 ...
语法的另一种用法:rest 参数。
休息参数
您将在本文中学习的最后一个特性是 剩余参数 语法。 语法看起来与spread (...
) 相同,但效果相反。 其余语法不会将数组或对象解包为单个值,而是创建一个包含不定数量参数的数组。
例如,在函数 restTest
中,如果我们希望 args
是一个由不定数量的参数组成的数组,我们可以有以下内容:
function restTest(...args) { console.log(args) } restTest(1, 2, 3, 4, 5, 6)
传递给 restTest
函数的所有参数现在都在 args
数组中可用:
Output[1, 2, 3, 4, 5, 6]
Rest 语法可以用作唯一的参数,也可以用作列表中的最后一个参数。 如果用作唯一参数,它将收集所有参数,但如果它位于列表的末尾,它将收集剩余的每个参数,如下例所示:
function restTest(one, two, ...args) { console.log(one) console.log(two) console.log(args) } restTest(1, 2, 3, 4, 5, 6)
这将单独获取前两个参数,然后将其余参数分组到一个数组中:
Output1 2 [3, 4, 5, 6]
在旧代码中,arguments
变量可用于收集传递给函数的所有参数:
function testArguments() { console.log(arguments) } testArguments('how', 'many', 'arguments')
这将给出以下输出:
[secondary_label Output]1 Arguments(3) ["how", "many", "arguments"]
然而,这有一些缺点。 首先,arguments
变量不能与箭头函数一起使用。
const testArguments = () => { console.log(arguments) } testArguments('how', 'many', 'arguments')
这会产生一个错误:
OutputUncaught ReferenceError: arguments is not defined
此外,arguments
不是真正的数组,如果不先转换为数组,则无法使用 map 和 filter 等方法。 它还将收集所有传递的参数,而不仅仅是其余的参数,如 restTest(one, two, ...args)
示例所示。
在解构数组时也可以使用 Rest:
const [firstTool, ...rest] = ['hammer', 'screwdriver', 'wrench'] console.log(firstTool) console.log(rest)
这将给出:
Outputhammer ["screwdriver", "wrench"]
解构对象时也可以使用 Rest:
const { isLoggedIn, ...rest } = { id: 1, name: 'Ben', isLoggedIn: true } console.log(isLoggedIn) console.log(rest)
给出以下输出:
Outputtrue {id: 1, name: "Ben"}
通过这种方式,rest 语法为收集不确定数量的项目提供了有效的方法。
结论
在本文中,您了解了解构、扩展语法和剩余参数。 总之:
- 解构用于从数组项或对象属性创建变量。
- 扩展语法用于解包可迭代对象,例如数组、对象和函数调用。
- Rest 参数语法将从不定数量的值创建一个数组。
解构、剩余参数和扩展语法是 JavaScript 中的有用功能,有助于保持代码简洁明了。
如果您想了解解构的实际效果,请查看 How To Customize React Components with Props,它使用此语法来解构数据并将其传递给自定义前端组件。 如果您想了解有关 JavaScript 的更多信息,请返回我们的 如何在 JavaScript 中编码系列页面。