深层和浅层卷积
Copying values seems somewhat trivial. Nevertheless, it’s almost impossible to find a developer that has never had an issue with some wrong reference, or pointer in the case of C-like languages. In this article, I’m going to focus on how to copy variables/values in JavaScript.
复制值似乎有些琐碎。 但是,要找到从未出现过错误引用或类似C语言的指针的问题的开发人员几乎是不可能的。 在本文中,我将重点介绍如何在JavaScript中复制变量/值。
原始值与参考值 (Primitive vs Reference Values)
原始值 (Primitive values)
In JavaScript, a primitive (primitive value, primitive data type) is data that is not an object and has no methods. They are the following:
在JavaScript中,原语(原始值,原始数据类型)是不是对象且没有方法的数据。 它们是:
- String 串
- Number 数
- BigInt 大整数
- Boolean 布尔型
- undefined 未定义
- Symbol 符号
- null (which indeed is a special case for every Object) null(这确实是每个对象的特例)
Primitive values are immutable — instead of changing them directly, we’re modifying a copy, without affecting the original. They are passed by value. They’re assigned to variables and passed into functions by copying their value.
基本值是不可变的-我们在不影响原始值的情况下修改了副本,而不是直接更改它们。 它们通过价值传递。 它们被分配给变量,并通过复制其值传递给函数。
let example = '0';
let example_copy = example;example = '1';console.log('example'); // '1'
console.log('example_copy'); // '0'
参考值 (Reference Values)
Reference values, objects, and arrays, which are a special type of an object, are a bit different. Contrary to primitives these are mutable. This means they have properties that we can access and change
引用值,对象和数组是对象的一种特殊类型,有些不同。 与原语相反,它们是可变的。 这意味着它们具有我们可以访问和更改的属性
They are passed by reference and no copies are made before passing them. This article is focused on these and different ways of copying them.
它们通过引用传递,并且在传递之前没有副本。 本文重点介绍这些以及不同的复制方式。
浅拷贝 (Shallow Copy)
Shallow copy means we just pass just the reference meaning only the memory addresses are copied.
浅复制意味着我们只传递参考,意味着仅复制内存地址。
Let’s create a basket object indicating we have 1 keyboard and 2 monitors as default values and trying to copy it.
让我们创建一个篮子对象,该对象指示我们有1个键盘和2个监视器作为默认值,并尝试复制它们。
let basket = { keyboard: 1, monitor: 2 };
let basketShallow = basket;basket.keyboard = 3;console.log(basket); // { keyboard: 3, monitor: 2 }
console.log(basketShallow); // { keyboard: 3, monitor: 2 }
Notice that the basket_shallow.keyboard value has changed. This happened because the value was copied by the reference and changing in one place will change for all variables sharing that object reference.
请注意,basket_shallow.keyboard值已更改。 发生这种情况是因为该值是由引用复制的,并且在一个地方更改将更改共享该对象引用的所有变量。
深拷贝 (Deep Copy)
Deep copy means not passing the element by reference but passing the actual values.
深层复制意味着不通过引用传递元素,而是传递实际值。
A deep copy will duplicate every object it encounters. The copy and the original object will not share anything, so it will be a clone of the original.
深拷贝将复制它遇到的每个对象。 副本和原始对象不会共享任何内容,因此它将是原始对象的副本。
选项1:Object.assign() (Option 1: Object.assign())
Mostly used before the spread operator was around. The first argument will be modified and returned so, in most cases, we want to pass an empty object, the second argument should be the object we want to copy.
通常在传播算子出现之前使用。 第一个参数将被修改并返回,因此,在大多数情况下,我们要传递一个空对象,第二个参数应该是我们要复制的对象。
let basket = { keyboard: 1, monitor: 2 };
let basketDeep = Object.assign({}, basket)basket.keyboard = 3;console.log(basket); // { keyboard: 3, monitor: 2 }
console.log(basketDeep); // { keyboard: 1, monitor: 2 }
选项2:点差算子 (Option 2: Spread Operator)
Introduced in ES6, It copies its own enumerable properties from a provided object onto a new object, and it’s short and simple. ‘Spreads’ out all of the values into a new object. You can use it as follows
它在ES6中引入,将其自己的可枚举属性从提供的对象复制到新的对象上,它简短而简单。 将所有值“散布”到一个新对象中。 您可以按以下方式使用它
let basket = { keyboard: 1, monitor: 2 };
let basketDeep = { ...basket };basket.keyboard = 3;console.log(basket); // { keyboard: 3, monitor: 2 }
console.log(basketDeep); // { keyboard: 1, monitor: 2 }
Notice: if you’re using an array, spread operator should be used with a square bracket, e.g. […array].
注意:如果您使用的是数组,则应在方括号中使用扩展运算符,例如[…array]。
嵌套深层复制 (Nested Deep Copy)
Both Object.assign() and the spread operator make deep copies of data if the data is not nested.
如果未嵌套数据,则Object.assign()和传播运算符均会复制数据的深层副本。
When you use them to copy a nested object, they will create a deep copy of the topmost data and a shallow copy of the nested data.
当您使用它们复制嵌套对象时,它们将创建最顶层数据的深层副本和嵌套数据的浅层副本。
let basket = { keyboard: { quantity: 1 } , monitor: { quantity: 2 } };
let basketDeep = { ...basket };basket.keyboard.quantity = 3;console.log(basket);
// { keyboard: { quantity: 3}, monitor: { quantity: 2 } }
console.log(basketDeep);
// { keyboard: { quantity: 3}, monitor: { quantity: 2 } }
Notice the previous example. keyboard.quantity was affected in both variables because the spread operator will copy values by reference when the variables are more than 1 dimension deep.
注意前面的示例。 两个变量都影响keyboard.quantity ,因为当变量的深度超过一维时,散布运算符将通过引用复制值。
JSON.parse(JSON.stringify()) (JSON.parse( JSON.stringify() ))
The easiest solution for this issue is to encode the object into a JSON string and then build it again into an object. See the example below:
解决此问题的最简单方法是将对象编码为JSON字符串,然后将其重新构建为对象。 请参阅以下示例:
let basket = {keyboard: { quantity: 1 } , monitor: { quantity: 2 }};
let basketDeep = JSON.parse( JSON.stringify(basket) );basket.keyboard.quantity = 3;console.log(basket);
// { keyboard: { quantity: 3}, monitor: { quantity: 2 } }
console.log(basketDeep);
// { keyboard: { quantity: 1}, monitor: { quantity: 2 } }
动态嵌套深度复制功能 (Dynamic Nested Deep Copy Function)
Alternatively, you can build a custom copy of a specific object structure and adapting the function to your object structure. This might be a tightly coupled approach to a single object and if you change anything for that object, you will, most probably, need to change that function as well. I don’t recommend it.
或者,您可以构建特定对象结构的自定义副本,并使功能适应您的对象结构。 这可能是对单个对象的紧密耦合方法,如果您更改了该对象的任何内容,则很可能也需要更改该功能。 我不推荐。
With that in mind, a dynamic implementation for deep copy of an object is presented below.
考虑到这一点,下面介绍了用于对象深层复制的动态实现。
function deepCopy(objToCopy) {
let res = {} ;
for (const property in objToCopy) {
if (isPrimitiveValue(objToCopy[property])) {
res[property] = objToCopy[property];
} else {
res[property] = deepCopy(objToCopy[property]);
}
}
return res;
}function isPrimitiveValue(value) {
return !(typeof value === 'object' && value !== null);
}
结论 (Conclusion)
Every developer should be aware of shallow and deep copy concepts and know exactly when each implementation is more appropriated.
每个开发人员都应该了解浅层和深层复制概念,并确切地知道何时更适合每种实现。
The spread operator is quite simple and powerful but for nested objects, as we have seen, is not a suitable solution. Consider each scenario and choose the best approach to your case.
散布运算符非常简单且功能强大,但是对于嵌套对象,如我们所见,这不是合适的解决方案。 考虑每种情况,然后选择适合您情况的最佳方法。
Hope this article helps to understand these concepts.
希望本文有助于理解这些概念。
翻译自: https://medium.com/weekly-webtips/javascript-shallow-vs-deep-copy-f1122b3a8192
深层和浅层卷积