您的位置:

解构赋值:深拷贝还是浅拷贝

  在JavaScript中,解构赋值是一项非常方便的操作。通过解构赋值,我们可以轻松地将数组或对象的成员值分配给变量。但是,使用解构赋值时,我们需要了解它所使用的是深拷贝还是浅拷贝的机制。在这篇文章中,我们将从多个方面详细阐述解构赋值的深拷贝和浅拷贝机制。

一、基础知识

  解构赋值是以“结构”为单位来进行赋值的操作。例如,我们可以使用数组或对象的键来将它们的值分配给变量,例如: ```javascript const arr = [1, 2, 3]; const [x, y, z] = arr; console.log(x, y, z); // 1 2 3 ``` ```javascript const obj = { foo: 'hello', bar: 'world' }; const {foo, bar} = obj; console.log(foo, bar); // hello world ```   解构赋值的核心是绑定。它会把右侧的值绑定到左侧的变量上。

二、基本类型

  在解构赋值中,基本类型是属于浅拷贝的范畴。具体来说,如果我们使用解构赋值将一个基本类型数据赋给另一个变量,那么这两个变量将指向同一个内存地址。例如: ```javascript let a = 'hello'; let b = a; // 直接复制 console.log(a, b); // hello hello a = 'world'; console.log(a, b); // world hello ```   在这个例子中,尽管我们改变了变量a的值,但变量b的值仍然是原始的“hello”值。这是因为基本类型的赋值时采用的是值传递,这种特性也被延续到了解构赋值中。   可以用下面的代码对解构赋值的浅拷贝进行证明: ```javascript let a = { foo: 'hello' }; let b = { ...a }; // 通过扩展运算符进行浅拷贝 console.log(a, b); // { foo: 'hello' } { foo: 'hello' } a.foo = 'world'; console.log(a, b); // { foo: 'world' } { foo: 'hello' } ```   在这个例子中,我们使用了扩展运算符进行了一个表面上是完整拷贝的操作,但其实这里只是进行了浅拷贝。当我们修改变量a的属性foo时,变量b并没有跟着修改,因为两个变量只是引用了同一个对象的地址。

三、引用类型

  引用类型是指对象、数组和函数。当我们用解构赋值时,它们属于深拷贝范畴。例如: ```javascript let a = { foo: { bar: 'hello' } }; let b = { ...a }; // 通过扩展运算符进行浅拷贝 console.log(a, b); // { foo: { bar: 'hello' } } { foo: { bar: 'hello' } } a.foo.bar = 'world'; console.log(a, b); // { foo: { bar: 'world' } } { foo: { bar: 'hello' } } ```   尽管我们使用了扩展运算符进行了浅拷贝,但当我们修改了变量a的属性时,变量b的引用关系并没有改变。因此,在JavaScript中,解构赋值对引用类型的赋值采用的是深拷贝。   同时我们可以使用Object.assign()或者JSON.parse()来进行深拷贝的复制操作。例如: ```javascript let a = { foo: { bar: 'hello' } }; let b = JSON.parse(JSON.stringify(a)); // 通过JSON来进行深拷贝复制 console.log(a, b); // { foo: { bar: 'hello' } } { foo: { bar: 'hello' } } a.foo.bar = 'world'; console.log(a, b); // { foo: { bar: 'world' } } { foo: { bar: 'hello' } } ```   这里通过JSON来进行深拷贝复制,可以看出深度复制是完整的。修改变量a的属性foo的子属性bar时,变量b的值没有跟着改动。

四、函数参数

  在JavaScript中,我们经常将对象作为函数参数进行传递。使用解构赋值可以让代码更加简洁易读。例如: ```javascript function foo({ x = 1, y = 2 } = {}) { console.log(x, y); } foo({ x: 3 }); // 3 2 foo(); // 1 2 ```   在这个例子中,我们将解构赋值作为函数的默认参数进行传递。通过这样的方式,我们可以在函数中使用解构赋值,同时也可以不传递参数。   需要注意的是,当我们使用默认参数时,如果没有传入参数,则会被默认赋值为undefined。这样,我们可以使用解构赋值的默认值机制来为变量提供一个默认值。例如: ```javascript function foo({ x = 1, y = 2 } = { x: 0, y: 0 }) { console.log(x, y); } foo({ x: 3 }); // 3 2 foo(); // 1 2 ```   在这个例子中,我们使用了解构赋值的默认参数,同时为传递参数的情况做了处理,使得其默认参数为x: 0, y: 0。

五、数组拼接

  在解构赋值中,我们可以使用剩余运算符来进行数组拼接。例如: ```javascript const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; const arr = [...arr1, ...arr2]; console.log(arr); // [1, 2, 3, 4, 5, 6] ```   当我们使用剩余运算符时,它所代表的是数组中的其余元素。由于解构赋值会浅拷贝数组元素,因此我们使用剩余运算符进行数组拼接时,其实是在创建一个新的数组,而不是在原有的数组上进行操作。

六、小结

  在这篇文章中,我们从多个方面详细阐述了解构赋值的深拷贝和浅拷贝机制。我们了解了基础知识、基本类型、引用类型、函数参数、数组拼接等内容。通过这样的介绍,我们可以更好地使用解构赋值,同时避免在编写代码的时候出现意想不到的结果。   代码示例:https://codepen.io/parkouroff/pen/ZELvwvK