为了账号安全,请及时绑定邮箱和手机立即绑定

如何正确克隆JavaScript对象?

/ 猿问

如何正确克隆JavaScript对象?

慕莱坞7535251 2019-05-23 13:17:42

如何正确克隆JavaScript对象?

我有一个对象,x。我想把它复制为对象y,这样改变y就不要修改了x。我意识到复制从内置JavaScript对象派生的对象将导致额外的,不需要的属性。这不是问题,因为我正在复制我自己的一个文字构造的对象。

如何正确克隆JavaScript对象?


查看完整描述

4 回答

?
慕慕0277861

为JavaScript中的任何对象执行此操作不会简单或直接。您将遇到错误地从对象原型中拾取属性的问题,该属性应保留在原型中而不会复制到新实例。例如,如果要添加clone方法Object.prototype,如某些答案所示,则需要显式跳过该属性。但是,如果还有其他额外的方法Object.prototype,或其他中间原型,你不知道怎么办?在这种情况下,您将复制不应该的属性,因此您需要使用该hasOwnProperty方法检测无法预料的非本地属性。

除了不可枚举的属性,当您尝试复制具有隐藏属性的对象时,您将遇到更严峻的问题。例如,prototype是函数的隐藏属性。此外,对象的原型使用属性进行引用,该属性__proto__也是隐藏的,并且不会通过迭代源对象属性的for / in循环进行复制。我认为__proto__可能是Firefox的JavaScript解释器特有的,它可能在其他浏览器中有所不同,但是你可以了解它。并非一切都是可以计算的。如果您知道其名称,则可以复制隐藏属性,但我不知道有任何方法可以自动发现它。

寻求优雅解决方案的另一个障碍是正确设置原型继承的问题。如果您的源对象的原型是Object,那么简单地创建一个新的通用对象{}将工作,但如果源的原型是其后代Object,那么您将错过使用hasOwnProperty过滤器跳过的原型中的其他成员,或者在原型中,但首先不是可枚举的。一种解决方案可能是调用源对象的constructor属性来获取初始复制对象,然后复制属性,但是您仍然不会获得不可枚举的属性。例如,Date对象将其数据存储为隐藏成员:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;}var d1 = new Date();/* Executes function after 5 seconds. */setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());}, 5000);

日期字符串d1将落后5秒d2。使一个Date与另一个相同的setTime方法是调用方法,但这是特定于Date类的。我不认为这个问题有一个防弹的一般解决方案,但我会很高兴出错!

当我不得不实施一般深度复制我最终通过假设我只需要复制一个普通的妥协ObjectArrayDateStringNumber,或Boolean。最后3种类型是不可变的,所以我可以执行浅拷贝而不用担心它会改变。我进一步假设该列表中包含的6个简单类型中的任何元素Object或者Array也将是其中之一。这可以使用以下代码完成:

function clone(obj) {
    var copy;

    // Handle the 3 simple types, and null or 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] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");}

只要对象和数组中的数据形成树结构,上述函数就可以适用于我提到的6种简单类型。也就是说,对象中的相同数据的引用不超过一个。例如:

// This would be cloneable:var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8};// This would kind-of work, but you would get 2 copies of the // inner node instead of 2 references to the same copyvar
     directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8};directedAcyclicGraph["right"] = directedAcyclicGraph["left"];// Cloning this would cause a stack overflow due to infinite
     recursion:var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8};cyclicGraph["right"] = cyclicGraph;

它将无法处理任何JavaScript对象,但它可能足以用于许多目的,只要您不认为它只适用于您抛出的任何内容。


查看完整回答
反对 回复 2019-05-23
?
缥缈止盈

如果您不在对象中使用函数,则可以使用以下非常简单的衬垫:

var cloneOfA = JSON.parse(JSON.stringify(a));

这适用于包含对象,数组,字符串,布尔值和数字的所有类型的对象。

另请参阅本文关于在向工作人员发送消息和从工作人员发布消息时使用的浏览器的结构化克隆算法。它还包含深度克隆功能。


查看完整回答
反对 回复 2019-05-23
?
慕妹3242003

使用jQuery,您可以使用extend进行浅层复制

var copiedObject = jQuery.extend({}, originalObject)

对copiedObject的后续更改不会影响originalObject,反之亦然。

或者进行深层复制

var copiedObject = jQuery.extend(true, {}, originalObject)


查看完整回答
反对 回复 2019-05-23
?
ABOUTYOU

在ECMAScript 6中有Object.assign方法,它将所有可枚举的自有属性的值从一个对象复制到另一个对象。例如:

var x = {myProp: "value"};var y = Object.assign({}, x);

但请注意,嵌套对象仍会被复制为引用。


查看完整回答
反对 回复 2019-05-23

添加回答

回复

举报

0/150
提交
取消
意见反馈 邀请有奖 帮助中心 APP下载
官方微信