一篇文帮你彻底搞懂扩展运算符

什么是扩展运算符号?

扩展运算符是一种操作符,它可以将我们可迭代对象扩展为单独的元素。常用于在函数调用、数组构造、构造字面量对象等情况下,将数组、类数组对象或字符串展开为单独的元素,或将对象展开为键值对。在语法层面展开数据

扩展运算符特性

  • 扩展运算符允许在其后接受表达式。
  • 在数组或函数参数中使用展开语法(...)时,该语法只能用来将可迭代对象(即部署了 Iterator 遍历器接口的对象)的数组表达式或字符串在语法层面进行展开操作。
  • 扩展运算符可用于展开/合并数组,将类数组对象转换为数组,并且可以用于接收参数。
  • 扩展运算符仅执行浅拷贝,这意味着它只会复制对象的引用,而不是对象本身的内容。
  • 在使用扩展运算符时,需要记住扩展运算符并不会克隆相同的属性

结合代码进行说明

扩展运算符允许在其后接受表达式

js 复制代码
// 设定变量 x 的值为 10
const x = 10;

// 构造数组 arr
const arr = [
  // 判断 x 是否大于 0,如果是则返回包含 'a' 的数组,否则返回空数组,然后使用扩展运算符将其展开
  ...(x > 0 ? ['a'] : []),
  // 将 'b' 加入数组 arr
  'b',
];

// 构造对象 obj
const obj = {
  // 判断 x 是否大于 1,如果是则返回包含属性 'a' 的对象,否则返回空对象,然后使用扩展运算符将其展开
  ...(x > 1 ? { a: 1 } : {}),
  // 添加属性 'b',值为 2
  b: 2,
};

// 输出数组 arr
console.log(arr); // ["a", "b"]

// 输出对象 obj
console.log(obj); // { a: 1, b: 2 }

扩展运算符可用于展开/合并数组,将类数组对象转换为数组,并且可以用于接收参数。

使用运算符展开数组

js 复制代码
const array1 = [1, 2, 3];
const array2 = [4, 5, ...array1, 6];

console.log(array2); // 输出:[4, 5, 1, 2, 3, 6]

使用运算符合并数组

示例一

js 复制代码
const coffee = ['coffee', 'water'];
const spices = ['cinnamon', 'nutmeg', 'cardamom'];

const coffeeReady = [...coffee, ...spices];

console.log(coffeeReady) 
// 输出:['coffee', 'water', 'cinnamon', 'nutmeg', 'cardamom'];

示例二

js 复制代码
const mySiblings = { 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
};

const myFamily = { 
  fathersName: 'Michael', 
  mothersName: 'Louise',
  ...mySiblings
};
// Now we can treat brothersName and sistersName as 
// a property of myFamily:
console.log(myFamily.brothersName) 

将类数组对象转换为数组

js 复制代码
// 类数组对象
const arrayLikeObject = { 0: 'a', 1: 'b', 2: 'c', length: 3 };

// 将类数组对象转换为数组
const array = [...arrayLikeObject];

console.log(array); // 输出:['a', 'b', 'c']

将字符串转换成数组

js 复制代码
const str = 'coffee';
const letters = [...str, 's.', '☕️']; 
console.log(letters);// ["c", "o", "f", "f", "e", "e", "s.", "☕️"]

使用运算符接收参数

js 复制代码
function myFunction(x, y, z) {
  console.log(x, y, z);
}

const args = [1, 2, 3];
myFunction(...args); // 输出:1 2 3

扩展运算符并不会克隆相同的属性

在下面的例子中,我们的vegetable1对象和vegetable2对象里都有一个相同的属性potato,但是跳转的值不同,在使用扩展运算符时,它并不会克隆相同的属性,而是会将后面对象中的属性值覆盖前面对象中的属性值。

js 复制代码
const vegetable1 = { 
  potato: '土豆', 
  tomato: '番茄',
  cucumber: '青瓜'
};
const vegetable2 = { 
  potato: '马铃薯', 
  cucumber: '黄瓜', 
  pumpkin: '南瓜', 
};

const allVegetable = { ...vegetable1, ...vegetable2 };
console.log(allVegetable);
/* 输出
{ 
  tomato: '番茄',
  cucumber: '青瓜'
  potato: '马铃薯', 
  cucumber: '黄瓜', 
  pumpkin: '南瓜', 
}
*/

扩展运算符仅执行浅拷贝

js 复制代码
let a = [1, [2, 3]]; // 定义数组 a,其中包含一个嵌套的数组 [2, 3]
const b = [4, 5, 6];

let c = [...a, ...b]; // 将数组 a 和数组 b 展开,并将它们的元素合并到数组 c 中
console.log(c);
// 输出: [1, [2, 3], 4, 5, 6]

a[0] = 11; // 修改数组 a 的第一个元素为 11
a[1][0] = 22; // 修改数组 a 中嵌套数组的第一个元素为 22

console.log(c);
// 输出: [1, [22, 3], 4, 5, 6]
  • 在上面的代码中,当我们使用扩展运算符 ... 将数组 a 和数组 b 展开到数组 c 中时,c 中的 [2, 3] 实际上是对数组 a 中嵌套数组的引用。因此,c 中的第二个元素 [2, 3] 并不是复制了数组 a 中的 [2, 3],而是引用了相同的嵌套数组。
  • 当我们修改数组 a 中的元素时,由于 c 中的嵌套数组和数组 a 中的嵌套数组是同一个引用,所以 c 中的嵌套数组也会被修改。
  • 因此,尽管我们只修改了数组 a,但数组 c 也受到了影响,其中嵌套数组的元素被修改为了 [22, 3]

注意:浅拷贝只能复制对象或数组的顶层结构。如果原始对象或数组中包含嵌套的对象或数组,浅拷贝后的对象或数组仍然会与原始对象或数组共享相同的嵌套对象或数组。这意味着在浅拷贝中修改嵌套对象或数组会影响到所有相关的对象或数组。

讲到这里肯定很多人会一想到,Object.assign(),它同样也能将另一个对象里的值拷贝到目标对象中,那他们之间有什么区别呢?

Object.assign() 和扩展运算的区别

特性 扩展运算符 (...) Object.assign()
语法 更简洁,直观 语法稍微复杂,接受目标对象和一个或多个源对象作为参数
返回值 返回一个新的对象或数组,其中包含了源对象或数组的属性或元素 返回目标对象本身,并且修改后的对象会直接反映在目标对象上
用途 用于对象和数组的浅拷贝,以及数组的合并 用于将一个或多个源对象的属性复制到目标对象中,也可以用于对象的浅拷贝和属性的合并
处理非对象类型 仅能处理对象和数组 能处理非对象类型的值,如字符串、数字、布尔值等,并将它们包装成对象
属性的设置方式 忽略源对象中的非自身可枚举属性 复制所有可枚举属性,包括原型链上的属性
相关推荐
Bellafu66636 分钟前
selenium 常用xpath写法
前端·selenium·测试工具
blackorbird3 小时前
Edge 浏览器 IE 模式成攻击突破口:黑客借仿冒网站诱导攻击
前端·edge
谷歌开发者4 小时前
Web 开发指向标 | Chrome 开发者工具学习资源 (一)
前端·chrome·学习
名字越长技术越强4 小时前
Chrome和IE获取本机ip地址
前端
天***88964 小时前
Chrome 安装失败且提示“无可用的更新” 或 “与服务器的连接意外终止”,Chrome 离线版下载安装教程
前端·chrome
半梦半醒*4 小时前
zabbix安装
linux·运维·前端·网络·zabbix
大怪v5 小时前
【搞发🌸活】不信书上那套理论!亲测Javascript能卡浏览器Reader一辈子~
javascript·html·浏览器
清羽_ls5 小时前
React Hooks 核心规则&自定义 Hooks
前端·react.js·hooks
你的人类朋友5 小时前
“签名”这个概念是非对称加密独有的吗?
前端·后端·安全
西陵5 小时前
Nx带来极致的前端开发体验——任务缓存
前端·javascript·架构