前端造轮子(二) JS中的深复制与浅复制

在读到这篇文章前,对浅复制的理解存在误区-。- js 深拷贝 vs 浅拷贝

  • 浅复制

    • 下面这段代码解释了浅复制与JS引用机制的区别
var obj1 = {
    name:'Richard',
    location:'Beijing',
    arr:['1','2','3'],
    arr2:['1',['2','3'],['4','5']]
}

var obj2 = obj1;

var shallowCopy = function (obj) {
    var copy = {}
    for (var prop in obj){
        if (obj.hasOwnProperty(prop)){
            copy[prop] = obj[prop]
        }
    }
    return copy
}
var obj3 = shallowCopy(obj1);
// 现在改变obj1的name属性
obj1.name = '八哥';
console.log(obj2.name)//八哥
console.log(obj3.name)//Richard

//现在改变obj1的arr属性
obj1.arr  = ['一','二','三']
console.log(obj2.arr);//['一','二','三']
console.log(obj3.arr);//['1','2','3']

//现在改变obj1的arr2[1],arr2[2]属性
obj1.arr2[1] = ['二','三']
obj1.arr2[2] = ['四','五']

console.log(obj3.arr2)//['1',['二','三'],['四','五']]
  • 结论

    • 浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。所以就会出现改变浅拷贝得到的 obj3 中的引用类型时,会使原始数据得到改变。
类型 和原对象是否指向同一对象 第一层数据为基本类型 原数据包含子对象
赋值 改变会使原数据一同改变 改变会使原数据一同改变
浅拷贝 改变会使原数据一同改变 改变会使原数据一同改变
深拷贝 改变会使原数据一同改变 改变会使原数据一同改变
  • 深复制

  • 思路

    • 递归调用浅复制,将所有属于对象的属性遍历赋予另外一个对象

    以下是Zepto的浅复制源码

 // 内部方法:用户合并一个或多个对象到第一个对象
   // 参数:
   // target 目标对象  对象都合并到target里
   // source 合并对象
   // deep 是否执行深度合并
   function extend(target, source, deep) {
       for (key in source)
           if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
               // source[key] 是对象,而 target[key] 不是对象, 则 target[key] = {} 初始化一下,否则递归会出错的
               if (isPlainObject(source[key]) && !isPlainObject(target[key]))
                   target[key] = {}

               // source[key] 是数组,而 target[key] 不是数组,则 target[key] = [] 初始化一下,否则递归会出错的
               if (isArray(source[key]) && !isArray(target[key]))
                   target[key] = []
               // 执行递归
               extend(target[key], source[key], deep)
           }
           // 不满足以上条件,说明 source[key] 是一般的值类型,直接赋值给 target 就是了
           else if (source[key] !== undefined) target[key] = source[key]
   }

   // Copy all but undefined properties from one or more
   // objects to the `target` object.
   $.extend = function(target){
       var deep, args = slice.call(arguments, 1);

       //第一个参数为boolean值时,表示是否深度合并
       if (typeof target == 'boolean') {
           deep = target;
           //target取第二个参数
           target = args.shift()
       }
       // 遍历后面的参数,都合并到target上
       args.forEach(function(arg){ extend(target, arg, deep) })
       return target
   }
  • 实现一个对象的深拷贝函数,需要考虑对象的元素类型以及对应的解决方案:
    • 基础类型:这种最简单,直接赋值即可
    • 对象类型:递归调用拷贝函数
    • 数组类型:数组中的元素可能是基础类型、对象还可能数组,因此要专门做一个函数来处理数组的深拷贝
/**
 * 数组的深拷贝实现
 * @param {Array} src
 * @param {Array} target
 **/
function cloneArr(src,target) {
   for (let item of src){
     if (Array.isArray(item)){
         target.push(cloneArr(item,[])); //新建一个空数组存放深复制的值
     }else if (typeof item  === 'object'){
         target.push(deepCopy(item,{}));
     }else {
         target.push(item)
     }
   }
   return target;
}

/**
 * 对象的深拷贝实现
 *@param {Object} src
 *@param {Object} target
 *@return {Object}
 **/
function deepCopy(src,target) {
    const keys = Reflect.ownKeys(src);
    //返回一个对象属性名的字符数组: [key1,key2,...]
    let value = null;
    for (let key of keys){   //ES6 创建一个循环迭代可迭代的对象
        value = src[key]
        if (Array.isArray(value)){
            target[key] = cloneArr(value,[]);
        }else if (typeof value === 'object'){
            target[key] = deepCopy(value,{});
        }else {
            target[key] = value
        }
    }
    return target;
}

var a = {
    name:'八哥',
    arr:[1,[2,3],[4,5]],
    obj:{x:1,y:2}
}
var b = {}
 deepCopy(a,b)

//以下为测试
a.arr[1] = [9,10];
console.log(b.arr[1]) //[2,3]
a.obj.x = 100;
console.log(b.obj.x) //1

你可能感兴趣的:(前端,JS)