suchen

Heim  >  Fragen und Antworten  >  Hauptteil

Wie klont man ein JavaScript-Objekt richtig?

<p>Ich habe ein Objekt <code>x</code>. Ich möchte es als Objekt <code>y</code> kopieren, damit Änderungen an <code>y</code> nicht geändert werden. Mir wurde klar, dass das Kopieren eines von einem integrierten JavaScript-Objekt abgeleiteten Objekts zusätzliche, unerwünschte Eigenschaften erzeugen würde. Dies ist kein Problem, da ich eines meiner eigenen textkonstruierten Objekte kopiere. </p> <p>Wie klont man ein JavaScript-Objekt richtig? </p>
P粉334721359P粉334721359506 Tage vor536

Antworte allen(2)Ich werde antworten

  • P粉691958181

    P粉6919581812023-08-24 09:44:45

    如果您在对象中不使用日期、函数、未定义、regExp 或 Infinity,则一个非常简单的行是 JSON.parse(JSON.stringify(object))< /代码>:

    const a = {
      string: 'string',
      number: 123,
      bool: false,
      nul: null,
      date: new Date(),  // stringified
      undef: undefined,  // lost
      inf: Infinity,  // forced to 'null'
    }
    console.log(a);
    console.log(typeof a.date);  // Date object
    const clone = JSON.parse(JSON.stringify(a));
    console.log(clone);
    console.log(typeof clone.date);  // result of .toISOString()

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

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

    Antwort
    0
  • P粉122932466

    P粉1229324662023-08-24 00:44:32

    2022 年更新

    有一个新的 JS 标准,称为结构化克隆。它适用于许多浏览器(请参阅我可以使用)。

    const clone = structuredClone(object);
    

    旧答案

    要对 JavaScript 中的任何对象执行此操作都不会简单或直接。您将遇到错误地从对象原型中获取属性的问题,这些属性应该保留在原型中而不是复制到新实例中。例如,如果您要向 Object.prototype 添加一个 clone 方法(如某些答案所述),则需要显式跳过该属性。但是,如果在 Object.prototype 或其他中间原型中添加了您不知道的其他附加方法怎么办?在这种情况下,您将复制不应该复制的属性,因此您需要使用 hasOwnProperty 方法。

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

    寻求优雅解决方案的另一个障碍是正确设置原型继承的问题。如果源对象的原型是 Object,则只需使用 {} 创建一个新的通用对象即可,但如果源对象的原型是 Object 的某个后代code>,那么您将丢失该原型中使用 hasOwnProperty 过滤器跳过的其他成员,或者原型中的其他成员,但一开始就不可枚举。一种解决方案可能是调用源对象的构造函数属性来获取初始复制对象,然后复制属性,但您仍然不会获得不可枚举的属性。例如,日期 对象将其数据存储为隐藏成员:

    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 的日期字符串将比 d2 晚 5 秒。使一个 Date 与另一个相同的方法是调用 setTime 方法,但这是特定于 Date 类的。我不认为这个问题有一个万无一失的通用解决方案,尽管我很乐意犯错!

    当我必须实现一般深度复制时,我最终妥协了,假设我只需要复制一个普通的ObjectArrayDate、<代码>字符串、<代码>数字或<代码>布尔。最后 3 种类型是不可变的,因此我可以执行浅复制而不用担心它会发生变化。我进一步假设 ObjectArray 中包含的任何元素也将是该列表中的 6 种简单类型之一。这可以通过如下代码来完成:

    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 copy
    var 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 对象,但它可能足以满足许多目的,只要您不认为它只适用于您扔给它的任何东西。

    Antwort
    0
  • StornierenAntwort