美文网首页技术挖掘机-soser
js对象浅拷贝和深拷贝

js对象浅拷贝和深拷贝

作者: fullbook | 来源:发表于2016-12-02 19:29 被阅读115次

    1、浅拷贝##

    拷贝就是把父对象的属性,全部拷贝给子对象。
    接下来,我们看一个拷贝的例子:
    <pre>
    function extendCopy(b) {
    var a = {};
    for (var i in b) {
    a[i] = b[i];
    }
    return a;
    }
    </pre>
    调用的时候,这样写:
    <pre>
    // 调用
    var copyA = {
    titleA: '标题A'
    };
    var copyB = extendCopy(copyA);
    console.log(copyB.titleA); // 标题A
    </pre>
    但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
    接下来,我们看一个篡改的示例:
    <pre>
    function extendCopy(b) {
    var a = {};
    for (var i in b) {
    a[i] = b[i];
    }
    return a;
    }

    // 调用
    var copyA = {
    arrayA: [1, 2, 3, 4]
    };
    var copyB = extendCopy(copyA);
    copyB.arrayA.push(5);
    console.log(copyA.arrayA); // [1, 2, 3, 4, 5]
    </pre>

    结果是增加了一个5。
    所以,extendCopy() 只是拷贝了基本类型的数据,我们把这种拷贝叫做“浅拷贝”。

    2、深拷贝##

    因为浅深拷有如此弊端所以我们接下来看一下深拷贝
    所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。
    <pre>
    function deepCopy(p, c) {
    var c = c || {};
    for (var i in p) {
    if (typeof p[i] === 'object') {
    c[i] = (p[i].constructor === Array) ? [] : {};
    deepCopy(p[i], c[i]);
    } else {
    c[i] = p[i];
    }
    }
    return c;
    }
    // 调用
    var copyA = {
    arrayA: [1, 2, 3, 4]
    };
    var copyB = deepCopy(copyA);
    copyB.arrayA.push(5);
    console.log(copyA.arrayA); // [1, 2, 3, 4]
    </pre>

    这样就完成了拷贝;

    拓展##

    拓展一、数组的深浅拷贝
    在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生。
    <pre>
    var arr = [1, 2, 3];
    var copyarr = arr;
    copyarr.push(4);
    console.log(arr); // [1, 2, 3, 4]
    console.log(copyarr); // [1, 2, 3, 4]
    </pre>

    像上面的这种直接赋值的方式就是浅拷贝,很多时候,这样并不是我们想要得到的结果,其实我们想要的是arr的值不变,不是吗?
    方法一:js的slice函数
    <pre>
    对于array对象的slice函数,
    返回一个数组的一段。(仍为数组)
    arrayObj.slice(start, [end])
    参数
    arrayObj
    必选项。一个 Array 对象。
    start
    必选项。arrayObj 中所指定的部分的开始元素是从零开始计算的下标。
    end
    可选项。arrayObj 中所指定的部分的结束元素是从零开始计算的下标。
    说明
    slice 方法返回一个 Array 对象,其中包含了 arrayObj 的指定部分。
    slice 方法一直复制到 end 所指定的元素,但是不包括该元素。如果 start 为负,将它作为 length + start处理,此处 length 为数组的长度。如果 end 为负,就将它作为 length + end 处理,此处 length 为数组的长度。如果省略 end ,那么 slice 方法将一直复制到 arrayObj 的结尾。如果 end 出现在 start 之前,不复制任何元素到新数组中。
    </pre>

    方法二:js的concat方法

    拓展二:$.extend()
    用过jquery的朋友都知道jquery中有$.extend()
    $.extend( [deep ], target, object1 [, objectN ] )

    ** deep **类型: Boolean 如果是true,合并成为递归(又叫做深拷贝)。
    ** target 类型: Object 对象扩展。这将接收新的属性。 object1 **类型: Object 一个对象,它包含额外的属性合并到第一个参数.
    ** objectN **类型: Object 包含额外的属性合并到第一个参数
    当我们提供两个或多个对象给$.extend(),对象的所有属性都添加到目标对象(target参数)。
    如果只有一个参数提供给$.extend(),这意味着目标参数被省略。在这种情况下,jQuery对象本身被默认为目标对象。这样,我们可以在jQuery的命名空间下添加新的功能。这对于插件开发者希望向 jQuery 中添加新函数时是很有用的。
    请记住,目标对象(第一个参数)将被修改,并且将通过$.extend()返回。然而,如果我们想保留原对象,我们可以通过传递一个空对象作为目标对象:
    var object = $.extend({}, object1, object2);
    在默认情况下,通过$.extend()合并操作不是递归的;如果第一个对象的属性本身是一个对象或数组,那么它将完全用第二个对象相同的key重写一个属性。这些值不会被合并。可以通过检查下面例子中 banana 的值,就可以了解这一点。然而,如果将 true 作为该函数的第一个参数,那么会在对象上进行递归的合并。
    警告:不支持第一个参数传递 false 。
    1、合并两个对象,并修改第一个对象。
    <pre>
    var obj1 = {
    name: 'name1',
    addr: {
    p: '浙江',
    c: '杭州'
    },
    age: 20
    };
    var obj2 = {
    addr: {
    d: '西湖'
    },
    sex: 1
    };

    $.extend(obj1, obj2);
    console.log(JSON.stringify(obj1));
    // {"name":"name1","addr":{"d":"西湖"},"age":20,"sex":1}
    </pre>
    2、采用递归方式合并两个对象,并修改第一个对象
    <pre>
    var obj1 = {
    name: 'name1',
    addr: {
    p: '浙江',
    c: '杭州'
    },
    age: 20
    };
    var obj2 = {
    name: 'name2',
    addr: {
    d: '西湖'
    },
    sex: 1
    };
    $.extend(true, obj1, obj2);
    console.log(JSON.stringify(obj1));
    // {"name":"name2","addr":{"p":"浙江","c":"杭州","d":"西湖"},"age":20,"sex":1}
    </pre>
    3、合并 defaults 和 options 对象,并且不修改 defaults 对象。这也是常用的插件开发模式。
    <pre>
    var defaults = { isAuto: false, limit: 5, name: "foo" };
    var options = { isAuto: true, name: "bar" };
    var settings = $.extend( {}, defaults, options );
    console.log(JSON.stringify( settings ));
    //settings -- {"isAuto":true,"limit":5,"name":"bar"}
    </pre>

    相关文章

      网友评论

        本文标题:js对象浅拷贝和深拷贝

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