gogogo
管理员
管理员
  • UID25
  • 粉丝0
  • 关注0
  • 发帖数1377
阅读:3541回复:1

js中的深拷贝与浅拷贝

楼主#
更多 发布于:2021-07-08 15:54
一、js的数据类型
1、基本数据类型:Number、String、Boolean、Undefined、Null (按值传递)
在修改a时并不会改到b
例如:
var a = 25;
var b = a;
b = 18;
console.log(a);//25
console.log(b);//18

2、引用数据类型:对象、数组(按引用传值)
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 }

复制一份obj1叫做obj2,然后把obj2.b改成100,但却不小心改到obj1.b,因为他们根本是同一个对象,这就是所谓的浅拷贝。


正确写法:
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 }

这样就是深拷贝,不会改到原本的obj1。



二、深拷贝与浅拷贝的区别
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
gogogo
管理员
管理员
  • UID25
  • 粉丝0
  • 关注0
  • 发帖数1377
沙发#
发布于:2021-07-08 15:58
三、浅拷贝的实现方法
1、简单地复制语句
2、Object.assign()
Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

Object.assign(target, ...sources)参数:
target:目标对象。
sources:任意多个源对象。
返回值:目标对象会被返回。


3、JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”比如:

(1)concat
(2)slice
(3)...展开运算符(... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。)

四、深拷贝的实现方法
1、手动复制
把一个对象的属性复制给另一个对象的属性



2、对象只有一层的话可以使用上面的:Object.assign()函数Object.assign({}, obj1)的意思是先建立一个空对象{},接着把obj1中所有的属性复制过去,所以obj2会长得跟obj1一样,这时候再修改obj2.b也不会影响obj1。
因为Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的 Deep Copy。不过如果要复制的对象只有一层的话可以考虑使用它。



3、转成 JSON 再转回来(深拷贝)用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
这样做是真正的Deep Copy,这种方法简单易用。
但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。

这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。
要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。



4、递归拷贝
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
  if(prop === obj) {
       continue;
}

if (typeof prop === 'object') {
       obj = (prop.constructor === Array) ? [] : {};
       arguments.callee(prop, obj);
} else {
       obj = 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;     // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
     if(prop === obj) {
              continue;
}

if (typeof prop === 'object') {
         obj = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
        obj = prop;
    }
}
return obj;
}

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

var $ = require('jquery');
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('lodash');
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
游客


返回顶部