JavaScript中拷贝对象方法总结

作者: csRyan | 来源:发表于2017-05-04 23:20 被阅读113次

    对象拷贝是在js中最基本的对象操作。

    浅拷贝

    function sallowCopy(source) {
        // source 不是对象,而是【原始类型】的情况
        // 原始类型说明详见http://www.jianshu.com/p/b161aeecb6d6
        if (null == source || "object" != typeof source) return source;
        
        // 其他情况都将 source 当作简单对象来处理
        var target = {};
        for (var key in source) {
            if (source.hasOwnProperty(key)) {  // 仅拷贝自身的属性
                target[key] = source[key];
            }
        }
        return target;
    }
    /*
    这个浅拷贝会将source对象上的所有[可枚举属性](http://www.jianshu.com/p/7b8da1db32b3)都拷贝到target对象上,不包括原型链上的属性。
    */
    

    浅复制仅仅复制嵌套对象的地址

    var outter = {
        outter_property:333,
        // inner 是嵌套对象
        inner: {
            inner_property:222
        }
    };
    
    var copy = sallowCopy(outter);
    // copy.inner 与 outter.inner 是同一个对象,它们指向同一个内存地址。
    
    copy.inner.inner_property = 'new value!';
    console.log(outter.inner.inner_property);   // new value!
    //改变了copy.inner对象,也就改变了outter.inner对象
    

    拷贝需要注意的问题有很多:

    • 需要拷贝的对象是Array
    • 需要拷贝的对象是Number、String、Boolean包装对象
    • 需要拷贝的对象是Date、RegExp等特别的内置对象
    • 需要拷贝的对象是Function
    • 需要拷贝的对象存在环,比如:
      var b = {}; 
      a.child = b;
      b.child = a;
      

    这些问题,上面这个方法都没有考虑,它只适用于简单对象的复制,这个方法仅用于示范浅拷贝的原理。事实上很难写出也没必要写出一个能够应付所有情况的完美方法。只要根据实际情况选择,往往都能找到一个满足需要的方法。文末会列举现有的、常见的拷贝函数。

    深拷贝

    function deepClone(obj) {
        var copy;
    
        // Handle number, boolean, string, null and undefined
        if (null == obj || "object" != typeof obj) return obj;
    
        // Handle Date
        if (obj instanceof Date) {
            copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
    
        // Handle Array
        if (obj instanceof Array) {
            copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = deepClone(obj[i]);
            }
            return copy;
        }
    
        // Handle Object
        if (obj instanceof Object) {
            copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]);
            }
            return copy;
        }
    
        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    
    

    深拷贝会复制嵌套的对象:

    var outter = {
        outter_property:333,
        inner: {
            inner_property:222
        }
    };
    
    var copy = deepClone(outter);
    // copy.inner 与 outter.inner是不同的两个对象,从此互不干涉
    copy.inner.inner_property = 'new value!';
    console.log(outter.inner.inner_property);   // 222
    // 修改copy.inner.inner_property,outter.inner.inner_property不会改变
    

    上面这个深拷贝方法除了可以处理原始类型和简单对象以外,还能处理Date和Array,依然不能处理Number对象、RegExp对象、Function对象等。不过已经比较实用了。

    现成的拷贝方法

    • var cloneOfA = JSON.parse(JSON.stringify(a));可以用于简单对象的深拷贝。这个方法的原理是将对象转换成json字符串以后再将json字符串转换成对象。因此要注意那些转换成json以后无法恢复的类型,最好只用来处理属性值是原始类型的对象和数组,或者它们的嵌套。此外,这个方法也不能处理存在环的对象。
    • Object.assign(target, ...sources)是ES6提供的浅拷贝方法,与我们给出的浅拷贝方法作用类似,拷贝对象自身的、可枚举的属性。Object.assign可以传入多个source对象,并且target不要求是空对象。需要注意的是它拷贝streing、number、boolean原始类型的时候,会先将它们装箱,再拷贝这个包装对象:
    Object.assign({}, 'abcd')
    // Object {0: "a", 1: "b", 2: "c", 3: "d"}
    
    • var copiedObject = jQuery.extend({}, originalObject) 是jQuery提供的方法。默认是浅拷贝,它拷贝自身和原型链上的所有可枚举属性。可以通过设置第一个参数为true来进行深拷贝:
      var copiedObject = jQuery.extend(true, {}, originalObject)

      extend、assign这些单词的名字的意思是“扩展”、“赋值”,拷贝对象只是它们的用途之一,它们的target参数不一定是要{}

    • Underscore的 _.clone(source) 浅拷贝,返回拷贝出的新对象。它拷贝自身和原型链上的所有可枚举属性。
    • lodash 的 _.clone(value) 和 _.cloneDeep()能够很好地处理很多内置对象:arrays, array buffers, booleans, date objects, maps, numbers, Object objects, regexes, sets, strings, symbols, and typed arrays,并且能处理存在环的对象,更接近完美。

    参考资料

    相关文章

      网友评论

      本文标题:JavaScript中拷贝对象方法总结

      本文链接:https://www.haomeiwen.com/subject/rupltxtx.html