各位靓仔靓女,大家好!我是你们今天的 JavaScript 解构赋值剩余运算符 (...
) 讲师——代码界的段子手。 今天咱们不搞那些虚头巴脑的,直接上干货,用最接地气的语言,把这个 ...
运算符给扒个精光!
啥是解构赋值?为啥要用剩余运算符?
首先,得知道解构赋值是干啥的。简单来说,它就是一种从对象或数组中提取数据,并赋给变量的语法。这比传统的方式更简洁,更优雅,也更装逼。
举个例子,假设我们有个对象:
const person = {
name: '张三',
age: 30,
city: '北京',
occupation: '码农',
hobby: '写Bug'
};
以前你想拿到 name
和 age
,可能得这么写:
const name = person.name;
const age = person.age;
现在有了结构赋值,你可以这样:
const { name, age } = person;
console.log(name, age); // 输出: 张三 30
看到了没?一行代码搞定!这就是解构赋值的魅力!
但是问题来了,如果我只想提取 name
和 age
,剩下的属性咋办?总不能一个个手动赋值吧?这时候,剩余运算符 ...
就该闪亮登场了!
剩余运算符:打包带走剩下的
剩余运算符,顾名思义,就是把剩下的东西打包带走。它在解构赋值中,可以把对象或数组中,没有被提取的属性或元素,收集到一个新的对象或数组中。
对象解构中的剩余运算符
还是上面的 person
对象,这次我们用剩余运算符把除了 name
和 age
之外的属性都收集起来:
const { name, age, ...rest } = person;
console.log(name, age); // 输出: 张三 30
console.log(rest); // 输出: { city: '北京', occupation: '码农', hobby: '写Bug' }
看,rest
变量就包含了 person
对象中除了 name
和 age
之外的所有属性。是不是很方便?
注意:
- 剩余运算符只能放在解构赋值的最后。
- 剩余运算符收集到的属性,会形成一个新的对象,不是原始对象的引用。 这意味着修改
rest
对象不会影响person
对象,反之亦然。
数组解构中的剩余运算符
数组解构和对象解构类似,只不过是操作的对象变成了数组。
假设我们有这样一个数组:
const numbers = [1, 2, 3, 4, 5];
我们可以用剩余运算符把除了前两个元素之外的元素收集起来:
const [first, second, ...remaining] = numbers;
console.log(first, second); // 输出: 1 2
console.log(remaining); // 输出: [3, 4, 5]
同样,remaining
变量包含了 numbers
数组中除了前两个元素之外的所有元素,组成了一个新的数组。
注意:
- 和对象解构一样,剩余运算符只能放在数组解构的最后。
- 剩余运算符收集到的元素,会形成一个新的数组,不是原始数组的引用。
剩余运算符的常见应用场景
剩余运算符在实际开发中有很多用处,下面列举几个常见的应用场景:
1. 提取部分属性,传递剩余属性给子组件
在 React 或 Vue 等前端框架中,经常需要把组件的 props 传递给子组件。有时候我们只需要提取部分 props,把剩下的 props 传递给子组件。
// React 组件示例
function ParentComponent(props) {
const { name, age, ...otherProps } = props;
return (
<div>
<h1>{name}</h1>
<p>Age: {age}</p>
<ChildComponent {...otherProps} /> {/* 把剩余的 props 传递给子组件 */}
</div>
);
}
function ChildComponent(props) {
return (
<div>
{/* 子组件可以使用父组件传递过来的 props */}
<p>City: {props.city}</p>
<p>Occupation: {props.occupation}</p>
</div>
);
}
在这个例子中,ParentComponent
提取了 name
和 age
属性,然后用剩余运算符把剩下的属性收集到 otherProps
对象中,最后通过 ...otherProps
的方式传递给 ChildComponent
。 这样,ChildComponent
就可以使用父组件传递过来的 city
和 occupation
等属性了。
2. 函数参数的处理
有时候我们需要定义一个函数,接受不定数量的参数。 剩余参数可以很方便地实现这个功能。
function sum(first, second, ...numbers) {
let result = first + second;
for (let number of numbers) {
result += number;
}
return result;
}
console.log(sum(1, 2, 3, 4, 5)); // 输出: 15
console.log(sum(1, 2)); // 输出: 3
在这个例子中,sum
函数接受至少两个参数 first
和 second
,剩下的参数都收集到 numbers
数组中。 这样,我们就可以传递任意数量的参数给 sum
函数了。
注意:
- 剩余参数只能是函数的最后一个参数。
- 剩余参数收集到的参数,会形成一个数组。
3. 过滤对象中的指定属性
有时候我们需要从一个对象中过滤掉一些指定的属性,只保留其他的属性。
function filterObject(obj, keysToOmit) {
const { [keysToOmit.join(',')]: omitted, ...rest } = obj; // 这里利用了计算属性名
return rest;
}
const myObject = { a: 1, b: 2, c: 3, d: 4 };
const filteredObject = filterObject(myObject, ['b', 'd']);
console.log(filteredObject); // 输出: { a: 1, c: 3 }
这个例子中,filterObject
函数接受一个对象 obj
和一个需要过滤的属性名数组 keysToOmit
。 利用计算属性名和剩余运算符,我们可以很方便地过滤掉指定的属性。
解释一下这行代码 const { [keysToOmit.join(',')]: omitted, ...rest } = obj;
keysToOmit.join(',')
: 将数组keysToOmit
中的元素用逗号连接成一个字符串。 例如,['b', 'd'].join(',')
的结果是"b,d"
。[keysToOmit.join(',')]
: 这里使用了计算属性名。 这意味着我们不是直接使用字符串"b,d"
作为属性名,而是计算出一个属性名。 但是,由于JavaScript对象的键只能是字符串或Symbol类型,直接这样写并不能达到目的,因为不会根据逗号分隔来删除对应的属性。实际上,它会尝试删除名为 "b,d" 的属性(如果存在的话)。 这种写法本身并不能直接删除多个属性,我们需要结合其他方式来实现。omitted
: 这是解构赋值的目标变量名。 但是在这个例子中,omitted
变量实际上并没有被使用,因为我们的目的是过滤掉指定的属性,而不是提取它们。...rest
: 剩余运算符,将obj
对象中除了keysToOmit.join(',')
对应的属性(实际上是尝试删除 "b,d" 属性)之外的所有属性,收集到一个新的对象rest
中。
更合适的实现方式
上面的代码并不能正确删除指定的多个属性,下面提供一个更合适的实现方式:
function filterObject(obj, keysToOmit) {
const newObj = { ...obj }; // 创建原始对象的副本,防止修改原始对象
for (const key of keysToOmit) {
delete newObj[key]; // 删除指定的属性
}
return newObj;
}
const myObject = { a: 1, b: 2, c: 3, d: 4 };
const filteredObject = filterObject(myObject, ['b', 'd']);
console.log(filteredObject); // 输出: { a: 1, c: 3 }
console.log(myObject); // 输出: { a: 1, b: 2, c: 3, d: 4 } (原始对象未被修改)
这个版本使用 delete
运算符来删除对象中的属性,并且首先创建了原始对象的副本,以避免修改原始对象。
4. 克隆对象
虽然剩余运算符不能完全深度克隆对象(对于嵌套对象,它仍然是浅拷贝),但在某些情况下,它可以作为一种快速的克隆对象的方式。
const originalObject = { a: 1, b: 2, c: { d: 3 } };
const clonedObject = { ...originalObject };
console.log(clonedObject); // 输出: { a: 1, b: 2, c: { d: 3 } }
clonedObject.a = 10;
console.log(originalObject.a); // 输出: 1 (originalObject 未被修改)
clonedObject.c.d = 30; // 修改嵌套对象
console.log(originalObject.c.d); // 输出: 30 (originalObject 也被修改了,因为是浅拷贝)
可以看到,修改 clonedObject
的 a
属性不会影响 originalObject
,但是修改 clonedObject
的 c.d
属性会影响 originalObject
,因为 c
属性是一个对象,剩余运算符只是浅拷贝了 c
属性的引用。
5. 合并对象
剩余运算符可以用于合并多个对象。
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const mergedObject = { ...obj1, ...obj2 };
console.log(mergedObject); // 输出: { a: 1, b: 2, c: 3, d: 4 }
如果多个对象中有相同的属性,后面的对象会覆盖前面的对象。
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const mergedObject = { ...obj1, ...obj2 };
console.log(mergedObject); // 输出: { a: 1, b: 3, c: 4 } (obj2 的 b 属性覆盖了 obj1 的 b 属性)
总结
总而言之,剩余运算符 ...
是 JavaScript 解构赋值中一个非常强大的工具,它可以帮助我们更方便地提取对象或数组中的数据,并把剩下的数据打包带走。 掌握了剩余运算符,可以写出更简洁,更优雅,更装逼的代码!
为了方便大家理解和查阅,下面用一个表格总结一下剩余运算符的特性:
特性 | 对象解构 | 数组解构 | 函数参数 |
---|---|---|---|
作用 | 收集对象中未被提取的属性 | 收集数组中未被提取的元素 | 收集函数中剩余的参数 |
位置 | 只能放在解构赋值的最后 | 只能放在解构赋值的最后 | 只能是函数的最后一个参数 |
类型 | 生成一个新的对象 | 生成一个新的数组 | 生成一个数组 |
引用 | 不是原始对象的引用 (浅拷贝) | 不是原始数组的引用 | |
应用场景 | 提取部分属性,传递剩余属性给子组件、过滤属性等 | 提取部分元素、处理不定数量的元素等 | 处理不定数量的参数等 |
希望今天的讲座对大家有所帮助。 记住,多敲代码,多练习,才能真正掌握这些技巧! 祝大家早日成为代码界的弄潮儿! 咱们下期再见!