指针嵌套指针 拷贝_浅拷贝与深拷贝

在 JavaScript 中有两中变量类型数据, 基本类型和引用类型. 对值类型的复制操作会对变量值进行拷贝, 两者互不相干. 而引用类型只会对存储变量的指针地址进行拷贝, 导致两个变量指向同一个地址, 也就是同一份数据, 修改其中一个会直接影响另外一个.

而我们要谈的浅拷贝与深拷贝就是专指引用类型.

浅拷贝

浅拷贝是最简单易理解的, 只对引用类型进行一级拷贝.

  • 自行实现
function shallowClone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            target[i] = source[i];
        }
    }


    return target;
} 
  • Object.assign
let obj = {
  a: 1,
  b: 2,
};
let objCopy = Object.assign({}, obj);
console.log(objCopy);
// Result - { a: 1, b: 2 }

上面的代码会将所有的可枚举的自身属性复制到目标对象.

  • 扩展操作符 (...)
var sourceObject = {
    a: 1,
    b: function() {
        return a;
    }
};
var targetObject = { ...sourceObject };
console.log(targetObject.b === sourceObject.b); // true

浅拷贝只会对一级进行拷贝, 对于嵌套的引用类型依然是同一个指针地址.

深拷贝

深拷贝会对任意的嵌套层级进行拷贝, 保证所有的引用类型全部是新对象, 不会和原对象有任何的关系, 彼此的修改不会互相影响.

  1. 奇淫巧技 JSON.parse(JSON.stringify(object))
let obj = { 
  a: 1,
  b: { 
    c: 2,
  },
}

let newObj = JSON.parse(JSON.stringify(obj));

obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } 

上面的方法虽然简单但是有很多的问题.

第一个: 不能拷贝函数

let obj = {
  name: 'neo',
  sayName: function exec() {
    return this.name;
  },
}
let method = JSON.parse(JSON.stringify(obj));
console.log(method); // JSON.parse(JSON.stringify(obj))
/*
{
  name: "neo"
}
*/

第二个: 循环引用

循环引用是说对象的对象引用了他们自身. 这个是深拷贝中一个老生常谈的问题.

// 循环引用
let obj = { 
  a: 'a',
  b: { 
    c: 'c',
    d: 'd',
  },
}


obj.c = obj.b;
obj.e = obj.a;
obj.b.c = obj.c;
obj.b.d = obj.b;
obj.b.e = obj.b.c;
let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj); 

上面代码执行的结果是:

11b5ea9d025ca356da609e8139a4a2c7.png

所以说, JSON.parse(JSON.stringify(obj)) 没办法解决循环引用的问题.

第三个: 值为 undefined 的属性会被忽略

var obj = {
    a: 'lendel',
    b: undefined
};
var newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
// 结果
/*
  { a: 'lendel' }
*/

第四个: Infinity 值会被置为 null

var obj = {
    a: 'lendel',
    i: Infinity
};
var newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
// 结果
/*
  { a: 'lendel', i: null }
*/

第五个: 一些对象会被转为字符串

这种方法会将一些类型的对象转变字符串, 例如 Date, Set, Map......

var obj = {
    a: 'lendel',
    d: new Date()
};
var newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
// 结果
/*
{ a: 'lendel', d: '2019-04-01T02:53:37.720Z' }
*/

一个简单的深拷贝方法

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

这是一段非常简单的深拷贝函数, 因为简单,所以问题也多. 参数的校验并不完备, 利用递归时当层次过深的时候容易造成栈溢出, 还有循环引用.

上面的代码体现了深拷贝的基本思想,但实际使用中建议使用 lodash 等现成的库

对于栈溢出的问题, 我们可以将递归改用循环来解决:

function loopClone(x) {
    const root = {};


    // 栈
    const loopList = [
        {
            parent: root,
            key: undefined,
            data: x
        }
    ];


    while (loopList.length) {
        // 深度优先
        const node = loopList.pop();
        const parent = node.parent;
        const key = node.key;
        const data = node.data;


        // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
        let res = parent;
        if (typeof key !== 'undefined') {
            res = parent[key] = {};
        }


        for (let k in data) {
            if (data.hasOwnProperty(k)) {
                if (typeof data[k] === 'object') {
                    // 下一次循环
                    loopList.push({
                        parent: res,
                        key: k,
                        data: data[k]
                    });
                } else {
                    res[k] = data[k];
                }
            }
        }
    }


    return roo
代码来源: https:// yanhaijing.com/javascri pt/2018/10/10/clone-deep/

结语:

写的时候我一直在想一个问题, 我究竟需不需要自己写一个深拷贝函数或类似的. 对于学习来说这是件无关紧要的事, 但是想要在产品中使用自己写的其实是挺蠢的事. 并不是说我们实现不出来, 而是投入时间的性价比实在太低. 要完成一个工业级别的函数需要大量的测试, 要覆盖到每一种情况,这是件得不偿失的事. 并且是在网上有那么多经过上千场景验证过的现成框架的情况下.所以, 要在合适的情况下选择合适的工具.

文章参考: https:// flaviocopes.com/how-to- clone-javascript-object/ https:// yanhaijing.com/javascri pt/2018/10/10/clone-deep/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值