JS深浅拷贝实现

  1. 引用赋值与浅拷贝的区别
    引用赋值是地址的赋值,将对象指针赋值给一个变量,让此变量指向对象。
    浅拷贝只会将对象的各个属性进行依次复制,并不会进行递归复制。
var obj = { a:1, arr: [2,3] };
var obj1 = {};
var shallowObj = {};
var deepObj = {};

//引用赋值,obj1指向obj所指向的对象
var obj1 = obj;

//浅拷贝实现
for (var prop in obj){
    if(obj.hasOwnProperty(prop)){
        shallowObj[prop] = obj[prop];
    }
}

//深拷贝,采用JSON.parse(JSON.stringify(obj))的方法实现,稍后讲解。
obj2 = JSON.parse(JSON.stringify(obj));

obj.arr[1] = 5;

console.log(obj1.arr[1]);//输出结果:5。其实,obj1与obj实为同一个对象
console.log(shallowObj.arr[1]);//输出结果:5。
console.log(deepObj.arr[1]); //输出结果:3。
  1. 浅拷贝与深拷贝的区别
    浅拷贝:因为浅拷贝只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,所以浅拷贝会导致 obj.arrshallowObj.arr 指向同一块内存地址,大概的示意图如下。意思就是将 obj 对象拷贝到 shallowObj 对象中,但不包括 obj 里面的子对象(arr),也就是说shallowObj 只保存了 obj 的子对象的指针,但同为一个子对象。
    在这里插入图片描述
    深拷贝:不仅复制原对象的各个属性,同时也将原对象各个属性所包含的子对象也递归地复制到新对象上。复制后两者是完全不相干的两个对象。将 obj 对象拷贝到 deepObj 对象中,包括 obj 里面的子对象。
    在这里插入图片描述
  2. 浅拷贝实现
    (1)遍历赋值实现
    var shallowCopy = function(obj) {
        // 只拷贝对象
        if (typeof obj !== 'object') return;
        // 根据obj的类型判断是新建一个数组还是对象
        var newObj = obj instanceof Array ? [] : {};
        // 遍历obj,并且判断是obj的属性才拷贝
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                newObj[key] = obj[key];
            }
        }
        return newObj;
    }

(2)ES6扩展运算符

var obj = { a:1, arr: [2,3] };
var obj1 = {...obj}

(3)数组方法(只适用于类数组对象)

Array.from(arrayLike)

从类数组对象或者可迭代对象中创建一个新的数组实例。返回一个新的数组。

var array1 = ['a', ['b', 'c'], 'd'];
var array2 = Array.from(array1);
array1[1][0] = 'e';

console.log(array2[1][0]);//输出结果为: "e"

Array.prototype.concat()

用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

var array1 = ['a', ['b', 'c'], 'd'];
var array2 = array1.concat();
array1[1][0] = 'e';

console.log(array2[1][0]);//输出结果为: "e"

Array.prototype.slice()

返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。

var array1 = ['a', ['b', 'c'], 'd'];
var array2 = array1.slice();
array1[1][0] = 'e';

console.log(array2[1][0]);//输出结果为: "e"

(4)ES6方法Object.assign()

用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

var obj = { a:1, arr: [2,3] };
var obj1 = Object.assign({}, obj);
obj.arr[1] = 5;

console.log(obj1.arr[1]);//输出结果:5。
  1. 深拷贝实现
    (1)借助JSON全局对象,然而使用这种方法会有一些隐藏的坑,它能正确处理的对象只有NumberStringBooleanArray,扁平对象,即那些能够被json直接表示的数据结构。
var obj = { a:1, arr: [2,3] };
var obj1 = JSON.parse(JSON.stringify(obj));
obj.arr[1] = 5;

console.log(deepObj.arr[1]); //输出结果:3。

局限性:

  • 会忽略 undefined
  • 会忽略 symbol
  • 不能序列化函数
  • 不能解决循环引用的对象
var arr = [function(){
    console.log(a)
}, {
    b: function(){
        console.log(b)
    }
}]

var new_arr = JSON.parse(JSON.stringify(arr));
console.log(new_arr);

执行结果:
在这里插入图片描述
(2)浅拷贝+递归:当遇到子对象是引用对象时,采用递归层层复制。

var deepCopy = function(obj) {
    if (typeof obj !== 'object') return;
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
    }
    return newObj;
}

var obj = { a:1, arr: [2,3] };
var obj1 = deepCopy(obj);
obj.arr[1] = 5;

console.log(obj.arr[1]); //输出结果:3。

存在问题:

  • 没有对传入参数进行校验,传入 null 时应该返回 null 而不是 {}
let newObj = deepCopy({value:null});
console.log(newObj);//输出结果为:{value:{}}
  1. 各语言深拷贝比较
    (1)jQuery —— $.clone() / $.extend()
    在这里插入图片描述
    (2)Underscore —— _.clone()
    在 Underscore 中有这样一个方法:_.clone(),这个方法实际上是一种浅复制 (shallow-copy),所有嵌套的对象和数组都是直接复制引用而并没有进行深复制。来看一下例子应该会更加直观:
    在这里插入图片描述
    (3)lodash —— _.clone() / _.cloneDeep()
    在lodash中关于复制的方法有两个,分别是_.clone()_.cloneDeep()。其中_.clone(obj, true)等价于_.cloneDeep(obj)。更厉害的是,lodash 针对存在环的对象的处理也是非常出色的。因此相较而言,lodash 在深复制上的行为反馈比前两个库好很多,是更拥抱未来的一个第三方库。

参考文章:
js 深拷贝 vs 浅拷贝
javascript中的深拷贝和浅拷贝?

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值