deepClone, extend, 深克隆对象和Object.assign(ES6)

本文探讨了在JavaScript中实现深克隆的方法,通过修改原始的$.extend函数,使其能够递归地拷贝对象属性,直至最深层级。此外,还对比了不同库提供的对象扩展功能,并介绍了ES6中Object.assign的局限性。

在看 you might not need jQuery的时候(外网打不开,这是个博客园转过来的),看到了$.extend函数来拷贝某个对象。测试了一下原文代码,居然复制不到深层的。原代码如下:

var deepExtend = function(out) {
  out = out || {};//问题就出在这里!

  for (var i = 1; i < arguments.length; i++) {
    var obj = arguments[i];

    if (!obj)
      continue;

    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (typeof obj[key] === 'object')
          deepExtend(out[key], obj[key]);
        else
          out[key] = obj[key];
      }
    }
  }

  return out;
};

deepExtend({}, objA, objB);

这里虽然直接对out赋值了,但可能出现的情况是,原始的target对象容器中(第一个参数,即Out),out[key]为undefined,那么如果确定out[key]的类型为对象的情况下,才是指向同一个对象,否则并没有改变out[key]的值。

修改代码如下,将这一步提到前面,同时润色代码增加报错信息:

function extend(out){

    if(!out){

        console.error('where is your container ?')
    }

    var objs = [].slice.call(arguments,1)

    if(objs.length > 0){

        objs.forEach(function(item,index){

            if(typeof item !== 'object'){

                console.error('item' + index + ' is no valid arguments, expected to be object')

            }

            else {

                for(var key in item){

                    if(item.hasOwnProperty(key)){

                        if(typeof item[key] === 'object'){

                            out[key]= out[key] || {}  // 这步是最重要的!

                            extend(out[key],item[key])

                        }

                        else{

                            out[key] = item[key]
                        }
                    }

                }

            }

        })

    }

    else{

        console.error('no objs to be copy')
    }

    return out;
}

var output = extend({a:{c:2},f:'fuck'},{a:1},{
    b:{
        lala:'name'
    }
})

这样就可以达到深克隆的目的了。当然除了JQuery, zepto、 Lodash、underScore、Prototype等库都实现了这个工具。ES6中的Object.assign也原生提供了相应的功能。

Object.assign() 方法值能复制可枚举、本身的属性(enumerable and own properties), 它用在引用源上使用get方法,在目标容器上使用set方法,引发了getters、setters。因此,如果在融合的源对象含有getter的话,会不适用。如果要复制属性的定义,包括其可枚举性,应该使用Object.getOwnPropertyDescriptor()和Object.defineProperty()。

对深克隆,不可使用Object.assign() 方法,因为它只会复制对象的引用而不是新建一个对象。最简单的深克隆方法某过于此,但只能复制可枚举类型的。

let target = JSON.parse(JSON.stringify(source))

因此,对深克隆,最好还是用extend方式层层递归,直到把源对象的属性拆到不是对象而是plain value为止。

### 使用 `Object.assign()` 实现对象深拷贝 `Object.assign()` 是一种浅拷贝方法,它会复制源对象的第一层属性,并将这些属性值直接分配到目标对象中。如果源对象的属性值是一个嵌套对象或数组,则目标对象中的对应属性将引用相同的内存地址,而不是创建一个新的副本[^2]。这意味着对目标对象中嵌套结构的修改会影响原始对象。 因此,**仅使用 `Object.assign()` 无法实现完整的深拷贝**。为了实现深拷贝,需要结合其他技术来处理嵌套对象数组。 #### 示例:使用递归与 `Object.assign()` 实现深拷贝 可以通过递归遍历对象的所有层级,并在每一层使用 `Object.assign()` 来构建新的对象,从而实现深拷贝: ```javascript function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } const cloned = Array.isArray(obj) ? [] : {}; Object.assign(cloned, obj); for (let key in cloned) { if (cloned.hasOwnProperty(key)) { cloned[key] = deepClone(cloned[key]); } } return cloned; } const original = { a: 1, b: { c: 2 } }; const copy = deepClone(original); copy.b.c = 3; console.log(original.b.c); // 输出 2,说明原对象未被修改 console.log(copy.b.c); // 输出 3,说明是独立的副本 ``` 该函数通过递归调用 `deepClone()` 处理嵌套对象或数组,并利用 `Object.assign()` 构建新对象的基础结构。这种方式确保了每个层级的对象都被独立复制,实现了真正的深拷贝[^4]。 #### 替代方案 若需更稳定或高效的深拷贝实现,可以考虑以下方式: - **JSON 序列化反序列化**:适用于不包含函数、Symbol 类型、undefined 值以及循环引用的对象。 ```javascript const copy = JSON.parse(JSON.stringify(original)); ``` 此方法简单但存在局限性,例如不能复制函数特殊对象(如 `Date`、`RegExp`)等。 - **第三方库**:如 Lodash 的 `_.cloneDeep` 方法提供了完善的深拷贝功能,支持复杂对象结构循环引用。 ```javascript const _ = require('lodash'); const copy = _.cloneDeep(original); ``` #### 总结 虽然 `Object.assign()` 本身是浅拷贝工具,但结合递归可以实现对象的深拷贝。对于更复杂的场景,推荐使用成熟的第三方库或 JSON 序列化等替代方案,以确保完整性稳定性[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值