Understanding the Spread Operator in JavaScript Diving in Merging Making copies

2025-06-07

理解 JavaScript 中的扩展运算符

深入探索

合并

制作复印件

{...object}

我第一次看到这个时,真的很困惑它的含义,甚至不知道它的作用,但利用 JavaScript 中扩展运算符的强大功能确实带来了回报。

在我最新的“初学者动手 Vue”系列教程中,我使用了扩展运算符,并承诺将发表一篇文章,向那些还不熟悉它的人解释它是什么以及它是如何工作的。

免责声明:扩展运算符是 ES6 的一项特性,因此并非所有浏览器都支持(IE 浏览器又一次受到支持)。不过,您可以放心,它在所有主流和现代浏览器中都能正常工作。浏览器兼容性

深入探索

MDN对此的解释如下:

扩展语法允许在需要零个或多个参数(对于函数调用)或元素(对于数组文字)的位置扩展可迭代对象(例如数组表达式或字符串),或者在需要零个或多个键值对(对于对象文字)的位置扩展对象表达式。

我不知道你怎么样,但对我来说,当我尝试学习所有这些知识时,感觉非常令人畏惧和困惑。所以,让我们把它分解成易于理解的部分。

假设你有一个函数,你想在其中添加三个值:

function add(a, b ,c) {
  return a + b + c;
}

现在想象一下,你有一个值数组:

const values = [1, 2, 3];

现在,如果我们问自己,该如何将这些值放入函数 add 中呢?一个实际的做法可能是这样做:

const sum = add(values[0], values[1], values[3]);

我的意思是,它有点儿用。对吧?但稍微想想,如果你有一个函数可以接受动态数量的参数,会发生什么?或者更简单一点,假设这个函数需要 20 个值,而我给你一个包含 20 个数字的数组,会发生什么?

这时事情就开始变得非常荒谬了,您要直接通过索引访问所有值吗?(reduce我知道,有办法使用数组函数来实现这一点,但为了举例子,请耐心听我说完。)

请考虑以下示例:

function sum(a,b,c,d,e,f,g,h,i,j,k,omgwhy) {
 return a+b+c+d+e+f+g+h+i+j+k+omgwhy;
}

const values = [1,2,3,4,5,6,7,8,9,11,12];

这就是展开运算符的闪光点!想象一下,这个values数组是一个盒子,它以非常整齐有序的方式包含所有数字,每个数字都有一个索引——并且完美地包含了它。

当我们像这样访问数组的索引值时,values[1]我们非常巧妙地取出了那个值,就像我们在第一个例子中所做的那样。但继续这个比喻,如果我们想按照盒子里的顺序取出所有值,该怎么办?

也许能够做这样的事情会很酷values[0-11],但我们却像这样输入...values

现在,在这种情况下,特别是对于该函数,你会说:

const result = sum(...values);

太棒了!现在,仔细观察,因为当从数组中取出值时,它们不仅仅是被抛到风中,它们都会进入函数中各自的参数。例如,a将获得1b将获得2等等。

合并

好吧,把数组展开到函数里确实不错。不过说实话我没怎么用过。不过合并两个数组或者两个对象就变得轻而易举了!

让我们举一个例子:

const favoriteGames = ['Zelda', 'Metroid'];
const favoriteBands = ['Queen', 'RHCP'];

const lifeFavorites = [...favoriteGames, ...favoriteBands];

首先,我们仅定义几个数组作为示例,favoriteGamesfavoriteBands。但让我们仔细看看第三行。

const lifeFavorites =我们将在这里存储合并的结果,没有什么太花哨的,我们将它设置为等于一个新的数组。

注意= [somestuffshere];

理解这一点非常重要,当你展开一个数组或对象时,如果这些展开的值需要以某种方式使用,你需要自行清理。所以在这种情况下,我们说:

好的,JS,获取这两个数组框中的内容,将它们中的所有垃圾转储到这个新框中,然后调用它lifeFavorites。值得庆幸的是,实际的垃圾转储过程非常有序,并且值的顺序也得到了保证。

因此实际结果将是:

['Zelda', 'Metroid', 'Queen', 'RHCP']

制作复印件

使用扩展运算符的另一个好方法是复制数组或对象。在 JavaScript 中,当你将对象或数组赋值给变量时,实际上是在存储一个叫做“指针”的东西,它的作用类似于对象存储位置的地址。

因此,如果你这样做:

const obj = {name: 'Marina'};
const anotherObj = obj;

这里最大的问题是objanotherObj实际上指向同一个对象。所以如果你改变其中一个,它们都会改变,因为这个讨厌的指针指向的是同一个对象。

在Vue这样的框架中,这会成为一个真正的问题,并且调试起来非常麻烦,因为在某些情况下,比如复制props时,你永远不想修改原始对象。在React中,更改 state 时也会用到这种方法,你不应该直接修改 value。

那么我们如何使用扩展运算符进行复制呢?

const baseObject = {name: 'Marina'};
const myCopy = {...baseObject};

const baseArray = [1, 2, 3];
const arrayCopy = [...baseArray];

需要注意的是,使用扩展运算符复制任何对象/数组时,复制操作都是浅复制。这意味着,即使你的对象有一个子对象,它child仍然会有一个指向原始对象的指针。(感谢所有在评论区指出这一点的朋友)

const original = { name: 'Parent', child: { name: 'Child' } };
const copy = {...original};

// The thing to note here is that the COPY will have a child property, but this
// child property is actually pointing to the same exact object as ORIGINAL

// So:
// original.child === copy.child

就是这样!再一次,我们将我们的盒子倒入另一个包含它的东西的盒子中,这将确保生成的对象/数组实际上是原始对象的副本——并且您可以安全地修改它们,而不必担心弄乱第一个。

文章来源:https://dev.to/marinamosti/understanding-the-spread-operator-in-javascript-485j
PREV
什么是 REST API - 你需要知道的一切
NEXT
那么 Vue.set 到底是什么?数据与响应式 陷阱 实际使用 Vue.set Vue 3.0