Home  >  Article  >  Web Front-end  >  An in-depth understanding of shallow copy and deep copy in JavaScript

An in-depth understanding of shallow copy and deep copy in JavaScript

零到壹度
零到壹度Original
2018-04-21 15:32:171085browse

The content of this article is about in-depth understanding of shallow copy and deep copy in JavaScript. It has certain reference value. Now I share it with you. Friends in need can refer to it

There are some basic types in JS like Number, String, Boolean, And objects are things like this {name: 'Larry', skill : 'Node.js' }, the biggest difference between objects and basic types lies in the way they pass values.

The basic type is passed by value, like this: When modifying a, it will not be changed to b

var a = 25;
var b = a;
b = 18;
console.log(a);//25
console.log(b);//18

, but the object is different, the object What is passed is passing by reference:

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = obj1;
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 100, c: 30 } <-- b 被改到了
console.log(obj2);
// { a: 10, b: 100, c: 30 }

Copy a copy of obj1 and call it obj2, then change obj2.b to 100 ,But accidentally changed to obj1.b,Because they are basically the same object, this is the so-called shallow copy.

To avoid such errors, write like this:

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- b 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }


This is a deep copy and will not change to the original obj1.

Shallow Copy VS Deep Copy

Shallow copy only copies points to a certain The pointer to the object does not copy the object itself. The old and new objects still share the same memory. However, deep copy will create an identical object. The new object does not share memory with the original object, and modifications to the new object will not change the original object.

How to implement shallow copy

That is, simply copy

1. Simply copy the statement

  <script type="text/javascript">
    function simpleClone(initalObj) {    
      var obj = {};    
      for ( var i in initalObj) {
        obj[i] = initalObj[i];
      }    
      return obj;
    }    var obj = {
      a: "hello",
      b:{
          a: "world",
          b: 21
        },
      c:["Bob", "Tom", "Jenny"],
      d:function() {
          alert("hello world");
        }
    }    var cloneObj = simpleClone(obj); 
    console.log(cloneObj.b); 
    console.log(cloneObj.c);
    console.log(cloneObj.d);

    cloneObj.b.a = "changed";
    cloneObj.c = [1, 2, 3];
    cloneObj.d = function() { alert("changed"); };
    console.log(obj.b);
    console.log(obj.c);
    console.log(obj.d);  </script>


The result is:

2. Object.assign()

<code> Object.assign is a new function in ES6. The Object.assign() method can copy any number of the source object's own enumerable properties to the target object, and then return the target object. But Object.assign() performs a shallow copy. What is copied is the reference to the object's attributes, not the object itself.

Object.assign(target, ...sources)

Parameters:

target: target object.
sources: any number of source objects.
Return value: The target object will be returned.

var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);

initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"

Compatibility:

Note:

Object.assign()可以处理一层的深度拷贝,如下:
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }


deep How to implement copy

If you want to completely copy without modifying the original object, you need to use Deep Copy. Here we will introduce several Deep Copy methods.

1. Manual copy

Copy the properties of one object to the properties of another object

var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }

But this is very troublesome, you have to do it one by one Copy; and this nature cannot be regarded as Deep Copy, because there may also be objects inside the object, such as the following situation:

var obj1 = { body: { a: 10 } };
var obj2 = { body: obj1.body };
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 20 } } <-- 被改到了
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// true

Although obj1 and obj2 are different objects, but they will share the same obj1.body, so when modifying obj2.body.a, the old one will also be modified.

2. If the object has only one layer, you can use the above: Object<span class="token punctuation">.<span class="token function">assign() function</span></span>

Object.assign({}, obj1) means to first create an empty object {}, and then copy all the properties in obj1, so obj2 will look the same as obj1. Modifying obj2.b at this time will not affect obj1.

Because Object.assign has the same effect as our manual copy, it can only process objects with only one layer of depth, and there is no way to achieve true Deep Copy. However, you can consider using it if there is only one layer of objects to be copied.

3. Convert to JSON and then back again

Use JSON.stringify to convert the object Convert it to a string, and then use JSON.parse to convert the string into a new object.

var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false

This is true Deep Copy, This method is simple and easy to use.

But this method also has many disadvantages, for example, it will discard the object's constructor. That is to say, after deep copying, no matter what the original constructor of the object is, it will become Object after deep copying.

The only objects that this method can correctly handle are Number, String, Boolean, Array, and flat objects, that is, those data structures that can be directly represented by json. RegExp objects cannot be deep copied in this way.

That is to say, only objects that can be converted into JSON format can be used in this way. Things like function cannot be converted into JSON.

var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);
// &#39;function&#39;
console.log(typeof obj2.fun);
// &#39;undefined&#39; <-- 没复制

要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。

4、递归拷贝

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    if (typeof initalObj[i] === &#39;object&#39;) {
      obj[i] = (initalObj[i].constructor === Array) ? [] : {};            
      arguments.callee(initalObj[i], obj[i]);
    } else {
      obj[i] = initalObj[i];
    }
  }    
  return obj;
}var str = {};var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。

为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。

改进版代码如下:

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === &#39;object&#39;) {
      obj[i] = (prop.constructor === Array) ? [] : {};            
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}var str = {};var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);


5、使用Object.create()方法

直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。

function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === &#39;object&#39;) {
      obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}


6、jquery

jquery 有提供一个$.extend可以用来做 Deep Copy。

var $ = require(&#39;jquery&#39;);var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);// false


7、lodash

另外一个很热门的函数库lodash,也有提供_.cloneDeep用来做 Deep Copy。

var _ = require(&#39;lodash&#39;);var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false

这个性能还不错,使用起来也很简单。

 

参考:

JavaScript 中对象的深拷贝

关于 JS 中的浅拷贝和深拷贝

iOS 深拷贝两种实现

The above is the detailed content of An in-depth understanding of shallow copy and deep copy in JavaScript. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn