粗浅的数据深拷贝的轮子

143 阅读1分钟

从一个变量给另一个变量赋值基本类型时,是从该变量上创建一个新值,然后复制到新变量分配的位置上。所以,新旧变量发生变化,是不会相互影响的。

但,当一个变量给另一个变量赋值引用类型时,也会复制一个值(变量标识符和指向堆内存的指针)到栈区内,他们指向的其实是同一个堆内存中的地址,所以他们当其一发生变化时,另一个变量将会受到影响。

以上知识点可以查看 数据类型和类型判断

所以,数据的深拷贝,是针对引用型变量来说的,常见的有 数组 和 对象。

function copy(obj, stack) {
    // 如果是基本类型的值/function/null 直接返回
    if (typeof obj !== 'object' || obj === null || obj === 'function') {
        return obj;
    }
    return deepCopy(obj, stack);
}

function deepCopy(obj, stack) {
    var className = Object.prototype.toString.call(obj);
    var result;
    stack = stack || [];
    var length = stack.length;
    while (length--) {
        // 解决循环引用拷贝出错,及特殊类型 regExp,Date 拷贝返回数据不对的问题
        if (stack[length] === obj || ['[object RegExp]', '[object Date]'].indexOf(className) > -1) return obj;
    }
    // 入栈
    stack.push(obj);
    if (className=== "[object Array]") {
      	// 递归
        result = obj.map(item => copy(item, stack));
    } else {
        result = {};
        for (let item in obj) {
            // 递归
            result[item] = copy(obj[item], stack);
        }
    }
    // 出栈
    stack.pop();
    return result;
}

当然,使用 JSON.parse(JSON.stringify(obj)) 也能解决大部分的问题,可以满足日常工作所需。

举几个 JSON.parse(JSON.stringify(obj)) 也无能为力的情况:

  • 循环引用
  • 正则对象
  • Date的值变成了字符串

上面的递归函数也有很多没考虑到的情况,可以参考这篇文章 JavaScript深拷贝的一些坑

所以,造轮子费心费力,更多是为了学习。

参考:

JavaScript深拷贝的一些坑