Comment cloner correctement un objet JavaScript ?
P粉334721359
P粉334721359 2023-08-23 12:26:59
0
2
491
<p>J'ai un objet <code>x</code>. Je souhaite le copier en tant qu'objet <code>y</code> afin que les modifications apportées à <code>y</code> J'ai réalisé que copier un objet dérivé d'un objet JavaScript intégré créerait des propriétés supplémentaires indésirables. Ce n'est pas un problème puisque je copie l'un de mes propres objets construits en texte. </p> <p>Comment cloner correctement un objet JavaScript ? </p>
P粉334721359
P粉334721359

répondre à tous(2)
P粉691958181

Si vous n'utilisez pas de date, de fonction, undéfini, regExp ou Infinity dans votre objet, une ligne très simple est 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()

Cela fonctionne pour tous les types d'objets, y compris les objets, les tableaux, les chaînes, les booléens et les nombres.

Voir également cet article sur le navigateur d'algorithmes de clonage structuré , utilisé lors de la publication de messages vers/depuis les travailleurs. Il inclut également une fonctionnalité de clonage profond.

P粉122932466

Mise à jour 2022

Il existe une nouvelle norme JS appelée Structured Cloning. Il fonctionne dans de nombreux navigateurs (voir Je peux l'utiliser).

const clone = structuredClone(object);

Ancienne réponse

Cela ne sera pas simple ou direct de faire cela avec n'importe quel objet en JavaScript. Vous rencontrerez le problème de l'obtention incorrecte des propriétés du prototype de l'objet, qui devraient rester dans le prototype au lieu d'être copiées dans la nouvelle instance. Par exemple, si vous optez pour la méthode Object.prototype 添加一个 clone 方法(如某些答案所述),则需要显式跳过该属性。但是,如果在 Object.prototype 或其他中间原型中添加了您不知道的其他附加方法怎么办?在这种情况下,您将复制不应该复制的属性,因此您需要使用 hasOwnProperty.

En plus des propriétés non énumérables, vous rencontrez également un problème plus délicat lorsque vous essayez de copier un objet avec des propriétés masquées. Par exemple, référencée par prototype 是函数的隐藏属性。此外,对象的原型是通过属性 __proto__ , cette propriété est également masquée et ne sera pas copiée par les boucles for/in qui parcourent les propriétés de l'objet source. Je pense que __proto__ peut être spécifique à l'interpréteur JavaScript de Firefox et peut être différent dans d'autres navigateurs, mais vous voyez l'idée. Tout n’est pas énumérable. Si vous connaissez le nom de la propriété cachée, vous pouvez la copier, mais je ne connais aucun moyen de la découvrir automatiquement.

Un autre obstacle dans la recherche d'une solution élégante est le problème de la configuration correcte de l'héritage prototypique. Si le prototype de l'objet source est Object,则只需使用 {} 创建一个新的通用对象即可,但如果源对象的原型是 Object 的某个后代code>,那么您将丢失该原型中使用 hasOwnProperty d'autres membres que le filtre ignore, ou d'autres membres qui sont dans le prototype mais qui n'étaient pas dénombrables en premier lieu. Une solution pourrait consister à appeler la propriété constructeur de l'objet source pour obtenir l'objet de copie initial, puis à copier les propriétés, mais vous n'obtiendrez toujours pas les propriétés non énumérables. Par exemple, l'objet Date code> stocke ses données en tant que membres masqués :

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);
La chaîne de date pour

d1 的日期字符串将比 d2 晚 5 秒。使一个 Date 与另一个相同的方法是调用 setTime 方法,但这是特定于 Dated1 sera 5 secondes plus tard que

d2. La façon de rendre une 🎜Date identique à une autre est d'appeler la méthode 🎜setTime, mais cela est spécifique à la classe 🎜Date. Je ne pense pas qu’il existe une solution universelle infaillible à ce problème, même si je suis heureux de me tromper ! 🎜

Lorsque j'ai dû implémenter une copie approfondie générale, j'ai fini par faire des compromis en supposant que j'avais juste besoin de copier une copie normale ObjectArrayDate代码>、字符串代码>、数字代码>或布尔代码>。最后 3 种类型是不可变的,因此我可以执行浅复制而不用担心它会发生变化。我进一步假设 ObjectArray et que tout élément contenu dans celle-ci serait également l'un des 6 types simples de cette liste. Cela peut être fait avec un code comme celui-ci :

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.");
}

Tant que les données de l'objet et du tableau forment une structure arborescente, la fonction ci-dessus est suffisante pour les 6 types simples que j'ai mentionnés. Autrement dit, les mêmes données dans l’objet ne sont référencées qu’une seule fois. Par exemple :

// 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;

Il ne gérera aucun objet JavaScript, mais il est probablement suffisant à de nombreuses fins, tant que vous ne pensez pas qu'il ne fonctionnera qu'avec tout ce que vous lui lancez.

Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal