web-dev-qa-db-fra.com

Comment cloner correctement un objet JavaScript?

J'ai un objet, x. J'aimerais le copier en tant qu'objet y, de sorte que les modifications apportées à y ne modifient pas x. Je me suis rendu compte que la copie d'objets dérivés d'objets JavaScript intégrés entraînerait des propriétés supplémentaires indésirables. Ce n'est pas un problème, car je copie l'un de mes propres objets, construits littéralement.

Comment cloner correctement un objet JavaScript?

2859
mindeavor

Si vous n'utilisez pas Dates, des fonctions indéfinies 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 tout type d'objets contenant des objets, des tableaux, des chaînes, des booléens et des nombres.

Voir aussi cet article sur l'algorithme de clone structuré des navigateurs qui est utilisé lors de la publication de messages à destination et en provenance d'un opérateur. Il contient également une fonction pour le clonage en profondeur.

894
heinob

Avec jQuery, vous pouvez une copie superficielle avec extend :

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

les modifications ultérieures apportées à la copiedObject n'affecteront pas la originalObject, et inversement.

Ou pour faire une copie complète :

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

Dans ECMAScript 6, il existe une méthode Object.assign , qui copie les valeurs de toutes les propriétés propres énumérables d'un objet à un autre. Par exemple:

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

Mais sachez que les objets imbriqués sont toujours copiés comme référence.

625

Par MDN :

  • Si vous voulez une copie superficielle, utilisez Object.assign({}, a)
  • Pour une copie "profonde", utilisez JSON.parse(JSON.stringify(a))

Il n’est pas nécessaire de recourir à des bibliothèques externes, mais vous devez vérifier compatibilité du navigateur en premier .

191
Tareq

Il y a beaucoup de réponses, mais aucune qui ne mentionne Object.create de ECMAScript 5, qui, certes, ne vous donne pas une copie exacte, mais définit la source comme prototype du nouvel objet.

Donc, ce n'est pas une réponse exacte à la question, mais c'est une solution en une ligne et donc élégante. Et cela fonctionne mieux pour 2 cas:

  1. Où un tel héritage est utile (duh!)
  2. Où l'objet source ne sera pas modifié, rendant ainsi la relation entre les 2 objets non problématique.

Exemple:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

Pourquoi est-ce que je considère que cette solution est supérieure? C'est natif, donc pas de boucle, pas de récursivité. Cependant, les anciens navigateurs auront besoin d'un polyfill.

128
itpastorn

Une manière élégante de cloner un objet Javascript dans une ligne de code

Une méthode Object.assign fait partie de la norme ECMAScript 2015 (ES6) et fait exactement ce dont vous avez besoin.

var clone = Object.assign({}, obj);

La méthode Object.assign () est utilisée pour copier les valeurs de toutes les propriétés propres énumérables d'un ou plusieurs objets source vers un objet cible.

Lire plus ...

Le polyfill pour supporter les navigateurs plus anciens:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}
119
Eugene Tiurin

La plupart des solutions sur Internet posent plusieurs problèmes. J'ai donc décidé de faire un suivi, y compris, pourquoi la réponse acceptée ne devrait pas être acceptée.

situation de départ

Je veux copier en profondeur un Javascript Object avec tous ses enfants et leurs enfants, etc. Mais comme je ne suis pas une sorte de développeur normal, mon Object a normal properties, circular structures et même nested objects .

Créons donc un circular structure et un nested object en premier.

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

Rassemblons tout dans un Object nommé a.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

Ensuite, nous voulons copier a dans une variable nommée b et le muter.

var b = a;

b.x = 'b';
b.nested.y = 'b';

Vous savez ce qui s’est passé ici car sinon, vous ne pourriez même pas vous poser la question.

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

Maintenant, trouvons une solution.

JSON

La première tentative que j'ai essayée utilisait JSON.

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

b.x = 'b';
b.nested.y = 'b';

Ne perdez pas trop de temps dessus, vous obtiendrez TypeError: Converting circular structure to JSON.

Copie récursive (la "réponse" acceptée)

Jetons un coup d'oeil à la réponse acceptée.

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

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

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

Ça a l'air bien, hein? C'est une copie récursive de l'objet et gère également d'autres types, comme Date, mais ce n'était pas une obligation.

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

La récursivité et circular structures ne fonctionnent pas bien ensemble ... RangeError: Maximum call stack size exceeded

solution native

Après s'être disputé avec mon collègue, mon patron nous a demandé ce qui s'était passé et il a trouvé une solution simple après une recherche sur Google. Cela s'appelle Object.create.

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

Cette solution a été ajoutée à Javascript il y a quelque temps et gère même circular structure.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

... et vous voyez, cela ne fonctionnait pas avec la structure imbriquée à l'intérieur.

polyfill pour la solution native

Il y a un polyfill pour Object.create dans l'ancien navigateur, tout comme le IE 8. C'est quelque chose qui est recommandé par Mozilla, et bien sûr, ce n'est pas parfait et pose le même problème que le solution native .

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

J'ai mis F hors du champ d'application afin que nous puissions examiner ce que instanceof nous dit.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

Même problème que la solution native , mais un résultat un peu moins bon.

la meilleure solution (mais pas parfaite)

En fouillant, j'ai trouvé une question similaire ( En Javascript, lors de la copie en profondeur, comment puis-je éviter un cycle, car une propriété est "this"? ) à celle-ci, mais avec un solution bien meilleure.

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

Et regardons la sortie ...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

Les exigences correspondent, mais il reste quelques problèmes mineurs, notamment la modification de instance sur nested et circ en Object.

La structure des arbres qui partagent une feuille ne sera pas copiée, ils deviendront deux feuilles indépendantes:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

conclusion

La dernière solution utilisant la récursivité et un cache n'est peut-être pas la meilleure solution, mais il s'agit d'une copie profonde réelle de l'objet. Il gère les simples properties, circular structures et nested object, mais il va gâcher leur instance lors du clonage.

jsfiddle

81
Fabio Poloni

Si vous acceptez une copie superficielle, la bibliothèque underscore.js a une méthode clone .

y = _.clone(x);

ou vous pouvez l'étendre comme

copiedObject = _.extend({},originalObject);
76
dule

OK imaginez que vous avez cet objet ci-dessous et que vous voulez le cloner:

let obj = {a:1, b:2, c:3}; //ES6

ou

var obj = {a:1, b:2, c:3}; //ES5

la réponse est principalement dépendante sur laquelle ECMAscript vous utilisez, dans ES6+, vous pouvez simplement utiliser Object.assign pour faire le clone:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

ou en utilisant l'opérateur spread comme ceci:

let cloned = {...obj}; //new {a:1, b:2, c:3};

Mais si vous utilisez ES5, vous pouvez utiliser quelques méthodes, mais le JSON.stringify, assurez-vous simplement que vous ne l'utilisez pas pour copier une grande quantité de données, mais il pourrait s'agir d'une seule ligne pratique dans de nombreux cas. , quelque chose comme ça:

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over
51
Alireza

Une solution particulièrement peu élégante consiste à utiliser le codage JSON pour créer des copies complètes d’objets n’ayant pas de méthodes membres. La méthodologie consiste à JSON encoder votre objet cible, puis en le décodant, vous obtenez la copie que vous recherchez. Vous pouvez décoder autant de fois que vous voulez en faire autant de copies que nécessaire.

Bien sûr, les fonctions n'appartenant pas à JSON, cela ne fonctionne donc que pour les objets sans méthodes membres.

Cette méthodologie était parfaite pour mon cas d'utilisation, car je stockais des blobs JSON dans un magasin de valeurs-clés, et lorsqu'ils sont exposés en tant qu'objets dans une API JavaScript, chaque objet contient en fait une copie de l'état d'origine de l'objet. peut calculer le delta après que l'appelant a muté l'objet exposé.

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value
38
Kris Walker

Vous pouvez simplement utiliser un propagation pour copier un objet sans références. Mais faites attention (voir les commentaires), la "copie" est juste au niveau le plus bas de l'objet/du tableau. Les propriétés imbriquées sont toujours des références!


Clone complet:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

Clone avec références au deuxième niveau:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScript ne prend pas en charge nativement les clones profonds. Utilisez une fonction utilitaire. Par exemple Ramda:

http://ramdajs.com/docs/#clone

34
musemind

Pour ceux qui utilisent AngularJS, il existe également une méthode directe de clonage ou d’extension des objets de cette bibliothèque.

var destination = angular.copy(source);

ou

angular.copy(source, destination);

Plus dans angular.copy documentation ...

25
Lukas Jelinek

La réponse de A.Levy est presque terminée, voici ma petite contribution: il existe un moyen de gérer les références récursives, voir cette ligne

if(this[attr]==this) copy[attr] = copy;

Si l'objet est un élément XML DOM, nous devons utiliser cloneNode à la place

if(this.cloneNode) return this.cloneNode(true);

Inspiré par l'étude exhaustive de A.Levy et l'approche de prototypage de Calvin, je propose cette solution:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

Voir aussi la note d'Andy Burke dans les réponses.

22
Jan Turoň

De cet article: Comment copier des tableaux et des objets en Javascript par Brian Huisman:

Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (var i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
      newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
  } return newObj;
};
19
Calvin

Voici une fonction que vous pouvez utiliser.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}
19
picardo

Dans ES-6, vous pouvez simplement utiliser Object.assign (...). Ex:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

Une bonne référence est ici: https://googlechrome.github.io/samples/object-assign-es6/

18
João Oliveira

Dans ECMAScript 2018

let objClone = { ...obj };

Sachez que les objets imbriqués sont toujours copiés à titre de référence.

17
Pavan Garre

Nouvelle réponse à une vieille question! Si vous avez le plaisir d'utiliser ECMAScript 2016 (ES6) avec Syntaxe de propagation , rien de plus simple.

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

Cela fournit une méthode propre pour une copie superficielle d'un objet. Faire une copie profonde, c'est-à-dire créer une nouvelle copie de chaque valeur de chaque objet imbriqué de manière récursive, nécessite l'une des solutions les plus lourdes ci-dessus.

JavaScript continue d'évoluer.

13
Charles Merriam

Vous pouvez cloner un objet et supprimer toute référence du précédent en utilisant une seule ligne de code. Faites simplement:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

Pour les navigateurs/moteurs qui ne prennent pas actuellement en charge Object.create, vous pouvez utiliser ce polyfill:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}
13
Rob Evans

Intéressé par le clonage d'objets simples:

JSON.parse(JSON.stringify(json_original));

Source: Comment copier un objet JavaScript dans une nouvelle variable PAS par référence?

11
Mohammed Akdim
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

La solution ES6 si vous voulez (peu profonde) cloner un instance de classe et pas seulement un objet de propriété.

11
flori

Utiliser Lodash:

var y = _.clone(x, true);
10
VaZaA

Je pense qu'il existe une réponse simple et efficace. En copie profonde, il y a deux préoccupations:

  1. Gardez les propriétés indépendantes les unes des autres.
  2. Et garder les méthodes en vie sur un objet cloné.

Donc, je pense qu'une solution simple sera tout d'abord de sérialiser et de désérialiser puis de faire une assignation dessus pour copier les fonctions aussi.

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

Bien que cette question apporte de nombreuses réponses, j'espère que celle-ci vous aidera également.

9
ConductedClever

Pour une copie en profondeur et un clone, JSON.stringify puis JSON.parse l'objet:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
7
Nishant Dwivedi
function clone(src, deep) {

    var toString = Object.prototype.toString;
    if(!src && typeof src != "object"){
        //any non-object ( Boolean, String, Number ), null, undefined, NaN
        return src;
    }

    //Honor native/custom clone methods
    if(src.clone && toString.call(src.clone) == "[object Function]"){
        return src.clone(deep);
    }

    //DOM Elements
    if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
        return src.cloneNode(deep);
    }

    //Date
    if(toString.call(src) == "[object Date]"){
        return new Date(src.getTime());
    }

    //RegExp
    if(toString.call(src) == "[object RegExp]"){
        return new RegExp(src);
    }

    //Function
    if(toString.call(src) == "[object Function]"){
        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });

    }

    var ret, index;
    //Array
    if(toString.call(src) == "[object Array]"){
        //[].slice(0) would soft clone
        ret = src.slice();
        if(deep){
            index = ret.length;
            while(index--){
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }

    return ret;
};
6
user1547016

Ceci est une adaptation du code de A. Levy pour gérer également le clonage de fonctions et de références multiples/cycliques. Cela signifie que si deux propriétés de l’arborescence clonée sont des références du même objet, l’arborescence de l’objet cloné aura ces propriétés. Les propriétés pointent sur un seul et même clone de l'objet référencé. Cela résout également le cas des dépendances cycliques qui, si elles ne sont pas gérées, conduisent à une boucle infinie. La complexité de l'algorithme est O (n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


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



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

Quelques tests rapides

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
6
Radu Simionescu

Je voulais juste ajouter à toutes les solutions Object.create de ce post, que cela ne fonctionne pas de la manière souhaitée avec nodejs.

Dans Firefox le résultat de

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

est

{test:"test"}.

Dans nodejs c'est

{}
6
heinob

J'ai écrit ma propre implémentation. Pas sûr que cela compte comme une meilleure solution:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

Voici la mise en œuvre:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].Push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}
6
yazjisuhail

(Ce qui suit était principalement une intégration de @ Maciej Bukowski , @ A. Levy , @ Jan Turoň , @ Red = = réponses, et les commentaires de @ LeviRoberts , @ RobG , merci beaucoup à eux !!!)

Copie profonde ? - OUI! (la plupart);
Copie peu profonde ? - NON! (sauf Proxy).

Je souhaite sincèrement la bienvenue à tous pour tester clone().
De plus, defineProp() est conçu pour facilement et rapidement (re) définir ou copier tout type de descripteur.

Une fonction

function clone(object) {
  /*
    Deep copy objects by value rather than by reference,
    exception: `Proxy`
  */

  const seen = new WeakMap()

  return (function clone(object) {
    if (object !== Object(object)) return object /*
    —— Check if the object belongs to a primitive data type */

    if (object instanceof Node) return object.cloneNode(true) /*
    —— Clone DOM trees */

    let _object // The clone of object

    switch (object.constructor) {
      case Object:
      case Array:
        _object = cloneObject(object)
        break

      case Date:
        _object = new Date(+object)
        break

      case Function:
        const fnStr = String(object)

        _object = new Function("return " +
          (/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr)
            ? "function " : ""
          ) + fnStr
        )()

        Object.defineProperties(_object,
          Object.getOwnPropertyDescriptors(object)
        )
        break

      default:
        switch (Object.prototype.toString.call(object.constructor)) {
          //                              // Stem from:
          case "[object Function]":       // `class`
          case "[object Undefined]":      // `Object.create(null)`
            _object = cloneObject(object)
            break

          default:                        // `Proxy`
            _object = object
        }
    }

    return _object


    function cloneObject(object) {
      if (seen.has(object)) return seen.get(object) /*
      —— Handle recursive references (circular structures) */

      const _object = Array.isArray(object)
        ? []
        : Object.create(Object.getPrototypeOf(object)) /*
          —— Assign [[Prototype]] for inheritance */

      seen.set(object, _object) /*
      —— Make `_object` the associative mirror of `object` */

      Reflect.ownKeys(object).forEach(key =>
        defineProp(_object, key, { value: clone(object[key]) }, object)
      )

      return _object
    }
  })(object)
}


function defineProp(object, key, descriptor = {}, copyFrom = {}) {
  const prevDesc = Object.getOwnPropertyDescriptor(object, key)
    || { configurable: true, writable: true }
    , copyDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
      || { configurable: true, writable: true } // Custom…
      || {} // …or left to native default settings

  const { configurable: _configurable, writable: _writable } = prevDesc
    , test = _writable === undefined
      ? _configurable // Can redefine property
      : _configurable && _writable // Can assign to property

  if (!test || arguments.length <= 2) return test;

  ["get", "set", "value", "writable", "enumerable", "configurable"]
    .forEach(k =>
      descriptor[k] === undefined && (descriptor[k] = copyDesc[k])
    )

  const { get, set, value, writable, enumerable, configurable }
    = descriptor

  return Object.defineProperty(object, key, {
    enumerable, configurable, ...get || set
      ? { get, set } // Accessor descriptor
      : { value, writable } // Data descriptor
  })
}

// tests

"use strict"
const obj0 = {

  u: undefined,

  nul: null,

  t: true,

  n: 9,

  str1: "string",

  str2: "",

  sym: Symbol("symbol"),

  [Symbol("e")]: Math.E,

  f: {
    getAccessorStr(object) {
      return []
        .concat(...
          Object.values(Object.getOwnPropertyDescriptors(object))
            .filter(desc => desc.writable === undefined)
            .map(desc => Object.values(desc))
        )
        .filter(prop => typeof prop === "function")
        .map(String)
    },
    f0: function f0() { },
    f1: function () { },
    f2: a => a / (a + 1),
    f3: () => 0,
    f4(params) { return param => param + params },
    f5: (a, b) => ({ c = 0 } = {}) => a + b + c
  },

  o: {
    n: 0,
    o: {
      f: function (...args) { }
    }
  },

  arr: [[0], [1, 2]],

  d: new Date(),

  get g() { return 0 }
}

defineProp(obj0, "s", {
  set(v) { this._s = v }
})
defineProp(obj0.arr, "tint", {
  value: { is: "non-enumerable" }
})
obj0.arr[0].name = "nested array"


let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", {
  set(v) { this._s = v + 1 }
})

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Routinely")

console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()

console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()

console.log("obj0\n ",
  ".arr.tint:", obj0.arr.tint, "\n ",
  ".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
  ".arr.tint:", obj1.arr.tint, "\n ",
  ".arr[0].name:", obj1.arr[0].name
)
console.log()

console.log("Accessor-type descriptor\n ",
  "of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
  "of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
  "set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
  "  → (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)

console.log("—— obj0 has not been interfered.")

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Circular structures")

obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr

obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)

console.log("Clear obj0's recursion:",
  obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
  "obj0\n ",
  ".o.r:", obj0.o.r, "\n ",
  ".arr:", obj0.arr
)
console.log(
  "obj1\n ",
  ".o.r:", obj1.o.r, "\n ",
  ".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")


console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Classes")

class Person {
  constructor(name) {
    this.name = name
  }
}

class Boy extends Person { }
Boy.prototype.sex = "M"

const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }

const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"

boy0.name = "one"
boy1.name = "neo"

console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
  Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)

Références

  1. Object.create() | MDN
  2. Object.defineProperties() | MDN
  3. Enumérable et propriété des propriétés | MDN
  4. TypeError: valeur d'objet cyclique | MDN

Astuces de langue utilisées

  1. Ajouter conditionnellement prop à object
5
ooo

La réponse de Jan Turoň ci-dessus est très proche et peut être la meilleure solution pour un navigateur en raison de problèmes de compatibilité, mais elle risque d'entraîner d'étranges problèmes d'énumération. Par exemple, en exécutant:

for ( var i in someArray ) { ... }

Assigne la méthode clone () à i après une itération dans les éléments du tableau. Voici une adaptation qui évite l'énumération et fonctionne avec node.js:

Object.defineProperty( Object.prototype, "clone", {
    value: function() {
        if ( this.cloneNode )
        {
            return this.cloneNode( true );
        }

        var copy = this instanceof Array ? [] : {};
        for( var attr in this )
        {
            if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
            {
                copy[ attr ] = this[ attr ];
            }
            else if ( this[ attr ] == this )
            {
                copy[ attr ] = copy;
            }
            else
            {
                copy[ attr ] = this[ attr ].clone();
            }
        }
        return copy;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

Cela évite de rendre la méthode clone () énumérable, car defineProperty () est par défaut enumerable en false.

5
Andy Burke

Puisque mindeavor a déclaré que l'objet à cloner est un objet 'construit littéralement', une solution pourrait être simplement générer l'objet plusieurs fois plutôt que de cloner une instance de l'objet:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
5
Bert Regelink

Consultez http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data pour le "passage sécurisé de données structurées du W3C" "algorithme, destiné à être mis en œuvre par les navigateurs pour transmettre des données à des travailleurs Web, par exemple. Cependant, il a certaines limites, dans la mesure où il ne gère pas les fonctions. Voir https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm pour plus d'informations, y compris un algorithme alternatif dans JS qui vous permet d'accéder à une partie du chemin.

4
user663031

Ci-dessous, ma version du clonage en profondeur, couvrant les fonctions et gérant les références circulaires.

https://github.com/radsimu/UaicNlpToolkit/blob/master/Modules/GGS/GGSEngine/src/main/resources/ro/uaic/info/nlptools/ggs/engine/core/jsInitCode.js# L17

4
Radu Simionescu

Utilisez deepcopy à partir de npm. Fonctionne à la fois dans le navigateur et dans node en tant que npm module...

https://www.npmjs.com/package/deepcopy

let a = deepcopy(b)

4
user3071643

Clonage structuré

Le standard HTML inclut un algorithme de clonage/sérialisation structuré interne pouvant créer des clones d'objets profonds. Il est toujours limité à certains types intégrés, mais en plus des quelques types pris en charge par JSON, il prend également en charge les dates, les régularisations, les cartes, les ensembles, les blobs, les listes de fichiers, les images, les tableaux fragmentés, les tableaux typés, et probablement davantage à l'avenir. . Il conserve également les références dans les données clonées, ce qui lui permet de prendre en charge des structures cycliques et récursives susceptibles de générer des erreurs pour JSON.

Support dans Node.js: Experimental ????

Le module v8 dans Node.js actuellement (à partir de Node 11) expose directement l'API de sérialisation structurée , mais cette fonctionnalité est toujours marquée comme "expérimentale", et sujet à modification ou suppression dans les versions futures. Si vous utilisez une version compatible, cloner un objet est aussi simple que:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

Prise en charge directe dans les navigateurs: peut-être éventuellement? ????

Les navigateurs ne fournissent pas actuellement d'interface directe pour l'algorithme de clonage structuré, mais une fonction globale structuredClone() a été décrite dans whatwg/html # 793 sur GitHub . Tel que proposé actuellement, son utilisation dans la plupart des cas serait aussi simple que:

const clone = structuredClone(original);

À moins que cela ne soit livré, les implémentations de clones structurés des navigateurs ne sont exposées qu'indirectement.

Solution de contournement asynchrone: utilisable. ????

Le moyen le moins coûteux de créer un clone structuré avec des API existantes consiste à publier les données via un port d'un MessageChannels . L'autre port émettra un événement message avec un clone structuré du .data attaché. Malheureusement, l’écoute de ces événements est nécessairement asynchrone et les alternatives synchrones sont moins pratiques.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Exemple d'utilisation:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

Solutions de contournement synchrones: Horrible! ????

Il n'y a pas de bonne option pour créer des clones structurés de manière synchrone. Voici quelques astuces peu pratiques à la place.

history.pushState() et history.replaceState() créent tous deux un clone structuré de leur premier argument et attribuent cette valeur à history.state. Vous pouvez l'utiliser pour créer un clone structuré de tout objet comme celui-ci:

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

Exemple d'utilisation:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();

Bien que synchrone, cela peut être extrêmement lent. Il entraîne tous les frais généraux associés à la manipulation de l'historique du navigateur. Si vous appelez cette méthode à plusieurs reprises, Chrome peut ne plus répondre temporairement.

Le constructeur Notification crée un clone structuré de ses données associées. Il tente également d'afficher une notification du navigateur à l'utilisateur, mais cela échouera silencieusement sauf si vous avez demandé une autorisation de notification. Si vous avez l'autorisation à d'autres fins, nous fermerons immédiatement la notification que nous avons créée.

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

Exemple d'utilisation:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();
3
Jeremy Banks

Cloner un objet basé sur un template. Que faites-vous si vous ne voulez pas une copie exacte, mais que vous voulez la robustesse d'une sorte d'opération de clonage fiable mais que vous ne voulez que des bits clonés ou que vous voulez vous assurer que vous pouvez contrôler l'existence ou le format de chaque valeur d'attribut cloné?

Je contribue ceci parce que c'est utile pour nous et nous l'avons créé parce que nous ne pouvions pas trouver quelque chose de similaire. Vous pouvez l'utiliser pour cloner un objet basé sur un objet template qui spécifie les attributs de l'objet que je veux cloner. Le modèle permet aux fonctions de transformer ces attributs en quelque chose de différent s'ils n'existent pas sur le serveur. objet source ou comme vous voulez gérer le clone. Si ce n'est pas utile, je suis sûr que quelqu'un peut supprimer cette réponse.

   function isFunction(functionToCheck) {
       var getType = {};
       return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
   }

   function cloneObjectByTemplate(obj, tpl, cloneConstructor) {
       if (typeof cloneConstructor === "undefined") {
           cloneConstructor = false;
       }
       if (obj == null || typeof (obj) != 'object') return obj;

       //if we have an array, work through it's contents and apply the template to each item...
       if (Array.isArray(obj)) {
           var ret = [];
           for (var i = 0; i < obj.length; i++) {
               ret.Push(cloneObjectByTemplate(obj[i], tpl, cloneConstructor));
           }
           return ret;
       }

       //otherwise we have an object...
       //var temp:any = {}; // obj.constructor(); // we can't call obj.constructor because TypeScript defines this, so if we are dealing with a TypeScript object it might reset values.
       var temp = cloneConstructor ? new obj.constructor() : {};

       for (var key in tpl) {
           //if we are provided with a function to determine the value of this property, call it...
           if (isFunction(tpl[key])) {
               temp[key] = tpl[key](obj); //assign the result of the function call, passing in the value
           } else {
               //if our object has this property...
               if (obj[key] != undefined) {
                   if (Array.isArray(obj[key])) {
                       temp[key] = [];
                       for (var i = 0; i < obj[key].length; i++) {
                           temp[key].Push(cloneObjectByTemplate(obj[key][i], tpl[key], cloneConstructor));
                       }
                   } else {
                       temp[key] = cloneObjectByTemplate(obj[key], tpl[key], cloneConstructor);
                   }
               }
           }
       }

       return temp;
   }

Une façon simple de l'appeler serait la suivante:

var source = {
       a: "whatever",
       b: {
           x: "yeah",
           y: "haha"
       }
   };
   var template = {
       a: true, //we want to clone "a"
       b: {
           x: true //we want to clone "b.x" too
       }
   }; 
   var destination = cloneObjectByTemplate(source, template);

Si vous souhaitez utiliser une fonction pour vous assurer qu'un attribut est renvoyé ou pour vous assurer qu'il s'agit d'un type particulier, utilisez un modèle comme celui-ci. Au lieu d'utiliser { ID: true }, nous fournissons une fonction qui ne fait que copier le ID attribute de l'objet source, tout en garantissant qu'il s'agit d'un nombre même s'il n'existe pas dans l'objet source.

 var template = {
    ID: function (srcObj) {
        if(srcObj.ID == undefined){ return -1; }
        return parseInt(srcObj.ID.toString());
    }
}

Les tableaux seront clonés correctement, mais si vous le souhaitez, vous pouvez également demander à votre propre fonction de gérer ces attributs individuels et de faire quelque chose de spécial comme celui-ci:

 var template = {
    tags: function (srcObj) {
        var tags = [];
        if (process.tags != undefined) {
            for (var i = 0; i < process.tags.length; i++) {

                tags.Push(cloneObjectByTemplate(
                  srcObj.tags[i],
                  { a : true, b : true } //another template for each item in the array
                );
            }
        }
        return tags;
    }
 }

Ainsi, dans ce qui précède, notre modèle copie simplement l'attribut tags de l'objet source s'il existe (il est supposé être un tableau), et pour chaque élément de ce tableau, la fonction clone est appelée pour le cloner individuellement en fonction sur un deuxième modèle qui copie simplement les attributs a et b de chacun de ces éléments de balise.

Si vous prenez des objets dans et hors du nœud et que vous voulez contrôler les attributs de ces objets qui sont clonés, il s'agit d'un excellent moyen de contrôler cela dans node.js et le code fonctionne également dans le navigateur.

Voici un exemple d'utilisation: http://jsfiddle.net/hjchyLt1/

3
Action Dan

Vous pouvez utiliser la fermeture fonctionnelle pour obtenir tous les avantages d'une copie en profondeur, sans copie profonde. C'est un paradigme très différent, mais qui fonctionne bien. Au lieu d'essayer de copier un objet existant, utilisez simplement une fonction pour instancier un nouvel objet lorsque vous en avez besoin.

Tout d'abord, créez une fonction qui retourne un objet

function template() {
  return {
    values: [1, 2, 3],
    nest: {x: {a: "a", b: "b"}, y: 100}
  };
}

Puis créez une fonction de copie superficielle simple

function copy(a, b) {
  Object.keys(b).forEach(function(key) {
    a[key] = b[key];
  });
}

Créez un nouvel objet et copiez-y les propriétés du modèle

var newObject = {}; 
copy(newObject, template());

Mais l'étape de copie ci-dessus n'est pas nécessaire. Tout ce que vous devez faire est ceci:

var newObject = template();

Maintenant que vous avez un nouvel objet, testez pour voir quelles sont ses propriétés:

console.log(Object.keys(newObject));

Cela affiche:

["values", "nest"]

Oui, ce sont les propriétés propres de newObject et non des références à des propriétés sur un autre objet. Vérifions simplement:

console.log(newObject.nest.x.b);

Cela affiche:

"b"

NewObject a acquis toutes les propriétés de l'objet modèle, mais est libre de toute chaîne de dépendance.

http://jsbin.com/ISUTIpoC/1/edit?js,console

J'ai ajouté cet exemple pour encourager le débat, alors merci d'ajouter quelques commentaires :)

3
d13

Selon le Guide de Style JavaScript Airbnb avec 404 contributeurs:

Préférez l'opérateur d'étalement d'objet à Object.assign pour les objets peu profonds. Utilisez l'opérateur rest object pour obtenir un nouvel objet avec certaines propriétés omises.

// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this

// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

J'aimerais également vous avertir que, même si Airbnb recommande à peine l'approche de propagation d'objet. Gardez à l'esprit que Microsoft Edge ne prend toujours pas en charge cette fonctionnalité 2018.

ES2016 + tableau Compat >>

3
Zsolt Gulyás

tilisez lodash _.cloneDeep ().

Copie superficielle: lodash _.clone ()

Une copie superficielle peut être réalisée en copiant simplement la référence.

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}

Shallow Copy: lodash _.clone()

Deep Copy: lodash _.cloneDeep ()

les champs sont déréférencés: plutôt que les références aux objets en cours de copie

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}

Deep Copy: lodash _.cloneDeep()

2
Ashok R

Je pense que récurrence avec la mise en cache est le meilleur que nous puissions faire ici sans bibliothèques.

Et la sous-estimation WeakMap soulève le problème des cycles, dans lesquels le stockage de paires de références à des objets anciens et nouveaux peut nous aider à recréer assez facilement des arbres entiers.

J'ai empêché le clonage en profondeur des éléments du DOM, probablement vous ne voulez pas cloner la page entière :)

function deepCopy(object) {
    const cache = new WeakMap(); // Map of old - new references

    function copy(obj) {
        if (typeof obj !== 'object' ||
            obj === null ||
            obj instanceof HTMLElement
        )
            return obj; // primitive value or HTMLElement

        if (obj instanceof Date) 
            return new Date().setTime(obj.getTime());

        if (obj instanceof RegExp) 
            return new RegExp(obj.source, obj.flags);

        if (cache.has(obj)) 
            return cache.get(obj);

        const result = obj instanceof Array ? [] : {};

        cache.set(obj, result); // store reference to object before the recursive starts

        if (obj instanceof Array) {
            for(const o of obj) {
                 result.Push(copy(o));
            }
            return result;
        }

        const keys = Object.keys(obj); 

        for (const key of keys)
            result[key] = copy(obj[key]);

        return result;
    }

    return copy(object);
}

Quelques tests:

// #1
const obj1 = { };
const obj2 = { };
obj1.obj2 = obj2;
obj2.obj1 = obj1; // Trivial circular reference

var copy = deepCopy(obj1);
copy == obj1 // false
copy.obj2 === obj1.obj2 // false
copy.obj2.obj1.obj2 // and so on - no error (correctly cloned).

// #2
const obj = { x: 0 }
const clone = deepCopy({ a: obj, b: obj });
clone.a == clone.b // true

// #3
const arr = [];
arr[0] = arr; // A little bit weird but who cares
clone = deepCopy(arr)
clone == arr // false;
clone[0][0][0][0] == clone // true;

NOTE: J'utilise des constantes pour for of loop, => operator et WeakMaps pour créer du code plus essentiel. Cette syntaxe (ES6) est prise en charge par les navigateurs actuels.

2
Maciej Bukowski

J'ai essayé ceci dans le cas d'un objet scalaire et cela fonctionne pour moi:

function binder(i) {
  return function () {
    return i;
  };
}

a=1;
b=binder(a)(); // copy value of a into b

alert(++a); // 2
alert(b); // still 1

Cordialement.

2
John Sonderson

Utilisation de par défaut (historiquement spécifique à nodejs mais maintenant utilisable depuis le navigateur grâce à JS moderne):

import defaults from 'object.defaults';

const myCopy = defaults({}, myObject);
1
aberaud

S'il n'y a pas de dépendances circulaires dans votre objet, je suggère d'utiliser l'une des autres réponses ou méthodes de copie de jQuery , car elles semblent toutes assez efficaces.

S'il y a des dépendances circulaires (c'est-à-dire que deux sous-objets sont liés l'un à l'autre), vous êtes un peu foutu comme il y en a (d'un point de vue théorique) aucun moyen de résoudre ce problème avec élégance .

1
Daniel Lew

Ok, cela pourrait être la meilleure option pour la copie superficielle. Si suit les nombreux exemples utilisant assign, mais conserve également l'héritage et le prototype. C’est aussi simple et fonctionne pour la plupart des objets et tableaux de type tableau, à l’exception de ceux nécessitant des constructeurs ou des propriétés en lecture seule. Mais cela signifie qu’il échoue lamentablement pour les versions de primitives TypedArrays, RegExp, Date, Maps, Sets et Object (Boolean, String, etc.).

function copy ( a ) { return Object.assign( new a.constructor, a ) }

a peut être n'importe quelle instance construite d'objet ou de classe, mais là encore, ne pas fiable pour les objets qui utilisent des accesseurs/gabarisseurs spécialisés ou qui ont des exigences de constructeur, mais pour des situations plus simples, cela bascule. Cela fonctionne également sur les arguments.

Vous pouvez également l'appliquer à des primitives pour obtenir des résultats étranges, mais alors ... sauf si cela finit par être un bidouillage utile, peu importe.

résultats des objets et tableaux intégrés de base ...

> a = { a: 'A', b: 'B', c: 'C', d: 'D' }
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> b = copy( a )
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> a = [1,2,3,4]
[ 1, 2, 3, 4 ]
> b = copy( a )
[ 1, 2, 3, 4 ]

Et échoue à cause des méchants get/setters, des arguments obligatoires du constructeur ou des propriétés en lecture seule, et des péchés contre le père.

> a = /\w+/g
/\w+/g
> b = copy( a )  // fails because source and flags are read-only
/(?:)/
> a = new Date ( '1/1/2001' )
2000-12-31T16:00:00.000Z
> b = copy( a )  // fails because Date using methods to get and set things
2017-02-04T14:44:13.990Z
> a = new Boolean( true )
[Boolean: true]
> b = copy( a )  // fails because of of sins against the father
[Boolean: false]
> a = new Number( 37 )
[Number: 37]
> b = copy( a )  // fails because of of sins against the father
[Number: 0]
> a = new String( 'four score and seven years ago our four fathers' )
[String: 'four score and seven years ago our four fathers']
> b = copy( a )  // fails because of of sins against the father
{ [String: ''] '0': 'f', '1': 'o', '2': 'u', '3': 'r', '4': ' ', '5': 's', '6': 'c', '7': 'o', '8': 'r', '9': 'e', '10': ' ', '11': 'a', '12': 'n', '13': 'd', '14': ' ', '15': 's', '16': 'e', '17': 'v', '18': 'e', '19': 'n', '20': ' ', '21': 'y', '22': 'e', '23': 'a', '24': 'r', '25': 's', '26': ' ', '27': 'a', '28': 'g', '29': 'o', '30': ' ', '31': 'o', '32': 'u', '33': 'r', '34': ' ', '35': 'f', '36': 'o', '37': 'u', '38': 'r', '39': ' ', '40': 'f', '41': 'a', '42': 't', '43': 'h', '44': 'e', '45': 'r', '46': 's' } 
1
Erich Horn

Voici une solution moderne qui ne présente pas les pièges de Object.assign() (ne copie pas par référence):

const cloneObj = (obj) => {
    return Object.keys(obj).reduce((dolly, key) => {
        dolly[key] = (obj[key].constructor === Object) ?
            cloneObj(obj[key]) :
            obj[key];
        return dolly;
    }, {});
};
1
ryanpcmcquen

La méthode Object.assign() est utilisée pour copier les valeurs de toutes les propriétés propres énumérables d'un ou plusieurs objets source vers un objet cible. Il retournera l'objet cible.

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }

Syntaxe

Object.assign(target, ...sources)

La méthode Object.assign() ne copie que les propriétés énumérables et propres d'un objet source vers un objet cible. Il utilise [[Get]] sur la source et [[Set]] sur la cible,

il invoquera donc les accesseurs et les setters. Par conséquent, il attribue des propriétés au lieu de copier ou de définir de nouvelles propriétés. Cela peut empêcher la fusion de nouvelles propriétés dans un prototype si les sources de fusion contiennent des getters. Pour copier les définitions de propriété, y compris leur énumération, dans les prototypes, Object.getOwnPropertyDescriptor() et Object.defineProperty() doivent être utilisés à la place.

Les propriétés String et Symbol sont copiées.

En cas d'erreur, par exemple, si une propriété n'est pas accessible en écriture, une erreur TypeError est générée et l'objet cible peut être modifié si des propriétés sont ajoutées avant qu'une erreur ne soit générée.

Notez que Object.assign() ne jette pas sur les valeurs source null ou undefined.

1
Ashish

Pour permettre une meilleure compréhension de la copie d’objets, ce jsbin illustratif peut être utile

class base {
  get under(){return true}
}

class a extends base {}

const b = {
  get b1(){return true},
  b: true
}

console.log('Object assign')
let t1 = Object.create(b)
t1.x = true
const c = Object.assign(t1, new a())
console.log(c.b1 ? 'prop value copied': 'prop value gone')
console.log(c.x ? 'assigned value copied': 'assigned value gone')
console.log(c.under ? 'inheritance ok': 'inheritance gone')
console.log(c.b1 ? 'get value unchanged' : 'get value lost')
c.b1 = false
console.log(c.b1? 'get unchanged' : 'get lost')
console.log('-----------------------------------')
console.log('Object assign  - order swopped')
t1 = Object.create(b)
t1.x = true
const d = Object.assign(new a(), t1)
console.log(d.b1 ? 'prop value copied': 'prop value gone')
console.log(d.x ? 'assigned value copied': 'assigned value gone')
console.log(d.under ? 'inheritance n/a': 'inheritance gone')
console.log(d.b1 ? 'get value copied' : 'get value lost')
d.b1 = false
console.log(d.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e = { ...t1, ...t2 }
console.log(e.b1 ? 'prop value copied': 'prop value gone')
console.log(e.x ? 'assigned value copied': 'assigned value gone')
console.log(e.under ? 'inheritance ok': 'inheritance gone')
console.log(e.b1 ? 'get value copied' : 'get value lost')
e.b1 = false
console.log(e.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator on getPrototypeOf')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e1 = { ...Object.getPrototypeOf(t1), ...Object.getPrototypeOf(t2) }
console.log(e1.b1 ? 'prop value copied': 'prop value gone')
console.log(e1.x ? 'assigned value copied': 'assigned value gone')
console.log(e1.under ? 'inheritance ok': 'inheritance gone')
console.log(e1.b1 ? 'get value copied' : 'get value lost')
e1.b1 = false
console.log(e1.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('keys, defineProperty, getOwnPropertyDescriptor')
f = Object.create(b)
t2 = new a()
f.x = 'a'
Object.keys(t2).forEach(key=> {
  Object.defineProperty(f,key,Object.getOwnPropertyDescriptor(t2, key))
})
console.log(f.b1 ? 'prop value copied': 'prop value gone')
console.log(f.x ? 'assigned value copied': 'assigned value gone')
console.log(f.under ? 'inheritance ok': 'inheritance gone')
console.log(f.b1 ? 'get value copied' : 'get value lost')
f.b1 = false
console.log(f.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('defineProperties, getOwnPropertyDescriptors')
let g = Object.create(b)
t2 = new a()
g.x = 'a'
Object.defineProperties(g,Object.getOwnPropertyDescriptors(t2))
console.log(g.b1 ? 'prop value copied': 'prop value gone')
console.log(g.x ? 'assigned value copied': 'assigned value gone')
console.log(g.under ? 'inheritance ok': 'inheritance gone')
console.log(g.b1 ? 'get value copied' : 'get value lost')
g.b1 = false
console.log(g.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
1
TrevTheDev

Dans mon code, je définis fréquemment une function (_) pour gérer les copies afin de pouvoir passer by value aux fonctions. Ce code crée une copie complète mais conserve l'héritage. Il assure également le suivi des sous-copies afin que les objets autoréférentiels puissent être copiés sans boucle infinie. Sentez-vous libre de l'utiliser.

Ce n'est peut-être pas le plus élégant, mais il ne m'a pas encore manqué.

_ = function(oReferance) {
  var aReferances = new Array();
  var getPrototypeOf = function(oObject) {
    if(typeof(Object.getPrototypeOf)!=="undefined") return Object.getPrototypeOf(oObject);
    var oTest = new Object();
    if(typeof(oObject.__proto__)!=="undefined"&&typeof(oTest.__proto__)!=="undefined"&&oTest.__proto__===Object.prototype) return oObject.__proto__;
    if(typeof(oObject.constructor)!=="undefined"&&typeof(oTest.constructor)!=="undefined"&&oTest.constructor===Object&&typeof(oObject.constructor.prototype)!=="undefined") return oObject.constructor.prototype;
    return Object.prototype;
  };
  var recursiveCopy = function(oSource) {
    if(typeof(oSource)!=="object") return oSource;
    if(oSource===null) return null;
    for(var i=0;i<aReferances.length;i++) if(aReferances[i][0]===oSource) return aReferances[i][1];
    var Copy = new Function();
    Copy.prototype = getPrototypeOf(oSource);
    var oCopy = new Copy();
    aReferances.Push([oSource,oCopy]);
    for(sPropertyName in oSource) if(oSource.hasOwnProperty(sPropertyName)) oCopy[sPropertyName] = recursiveCopy(oSource[sPropertyName]);
    return oCopy;
  };
  return recursiveCopy(oReferance);
};

// Examples:
Wigit = function(){};
Wigit.prototype.bInThePrototype = true;
A = new Wigit();
A.nCoolNumber = 7;
B = _(A);
B.nCoolNumber = 8; // A.nCoolNumber is still 7
B.bInThePrototype // true
B instanceof Wigit // true
1
Alec

Le problème avec la copie d'un objet qui, éventuellement, peut pointer sur lui-même, peut être résolu avec une simple vérification. Ajoutez cette vérification chaque fois qu'il y a une action de copie. Cela peut être lent, mais cela devrait fonctionner.

J'utilise une fonction toType () pour renvoyer le type d'objet, explicitement. J'ai aussi ma propre fonction copyObj (), qui est assez similaire en logique, qui répond aux trois observations Object (), Array () et Date ().

Je le lance dans NodeJS.

NON TESTÉ, ENCORE.

// Returns true, if one of the parent's children is the target.
// This is useful, for avoiding copyObj() through an infinite loop!
function isChild(target, parent) {
  if (toType(parent) == '[object Object]') {
    for (var name in parent) {
      var curProperty = parent[name];

      // Direct child.
      if (curProperty = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {
        if (isChild(target, curProperty)) return true;
      }
    }
  } else if (toType(parent) == '[object Array]') {
    for (var i=0; i < parent.length; i++) {
      var curItem = parent[i];

      // Direct child.
      if (curItem = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {
        if (isChild(target, curItem)) return true;
      }
    }
  }

  return false;     // Not the target.
}
0
James Koss

Je ne sais pas dans quels cas cela ne fonctionne pas, mais cela m'a donné une copie d'un tableau. Je pense que c'est mignon :) J'espère que ça aide

copiedArr = origArr.filter(function(x){return true})
0
knowingpark

Pour manipuler des objets circulaires que JSON.stringify ne peut pas gérer, vous pouvez importer une bibliothèque appelée JSOG , qui sérialise et désérialise les graphiques arbitraires au format JSON.

var clone = JSOG.parse(JSOG.stringify(original));

Il pourrait également être intéressant d’essayer d’appliquer cette astuce à JSOG pour le clonage (n’avez pas le temps, mais si quelqu'un veut tenter le coup ...):

Sérialiser une fonction simple:

foo.f = function(a) { return a }
var stringForm = foo.f.toString() // "function (a) { return a }"

Désérialiser une fonction:

eval("foo.f = " + stringForm)

Certaines conventions (probablement au nom de la propriété) pour identifier les fonctions par rapport aux chaînes régulières seraient nécessaires (@func_f peut-être).

Bien sûr, si la fonction appelle une deuxième fonction, celle-ci devra exister exactement comme pour l'originale.

Ce qui précède est cependant assez dangereux si vous acceptez le formulaire sérialisé d'une source non fiable, mais accepter toute fonction sous une forme quelconque provenant d'une source non fiable serait dangereux. Par conséquent, si vous êtes intéressé par des fonctions de clonage, la confiance doit déjà avoir été établie. (ou vous avez déjà l'intention d'écrire une faille de sécurité!).

Avertissement: Je n'ai pas testé la vitesse de JSOG stringify/parse vs JSON stringify/parse, mais cela fonctionne sur les objets simples (circulaires) que j'ai testés. avec.

0
Gus

Si vous avez un objet avec des fonctions, vous pouvez le faire avec JSONfn, voir http://www.eslinstructor.net/jsonfn/ .

var obj= {
    name:'Marvin',
    getName :  function(){
      return this.name;
    }
}
var cobj = JSONfn.parse(JSONfn.stringify(obj));
0
basis

Vous pouvez cloner votre objet sans modification objet parent -

    /** [Object Extend]*/
    ( typeof Object.extend === 'function' ? undefined : ( Object.extend = function ( destination, source ) {
        for ( var property in source )
            destination[property] = source[property];
        return destination;
    } ) );
    /** [/Object Extend]*/
    /** [Object clone]*/
    ( typeof Object.clone === 'function' ? undefined : ( Object.clone = function ( object ) {
        return this.extend( {}, object );
    } ) );
    /** [/Object clone]*/

    let myObj = {
        a:1, b:2, c:3, d:{
            a:1, b:2, c:3
        }
    };

    let clone = Object.clone( myObj );

    clone.a = 10;

    console.log('clone.a==>', clone.a); //==> 10

    console.log('myObj.a==>', myObj.a); //==> 1 // object not modified here

    let clone2 = Object.clone( clone );

    clone2.a = 20;

    console.log('clone2.a==>', clone2.a); //==> 20

    console.log('clone.a==>', clone.a); //==> 10 // object not modified here
0
RKTUXYN

Je suis venu sur cette page à cause de la même question, mais je n'utilise ni JQuery, ni aucune des méthodes de clonage n'a fonctionné pour mes propres objets.

Je suis conscient que ma réponse n'est pas trop liée à cette question parce que c'est une approche différente. Au lieu d'utiliser des fonctions de clonage, j'utilise une fonction de création. Cela a fonctionné pour moi pour les objectifs suivants (malheureusement restrictifs):

  1. J'utilise principalement du Javascript généré par JSP
  2. Je sais au début quel objet doit être généré (dans mon cas, il s'agit des informations d'une base de données qui sont extraites une fois et doivent être déployées plus souvent dans le JS.

J'ai d'abord défini mes objets comme ceci:

var obj= new Object();
obj.Type='Row';
obj.ID=1;
obj.Value='Blah blah';

Maintenant j'ai tout déplacé comme:

function getObjSelektor(id_nummer,selected){
var obj = document.createElement("select");
obj.setAttribute("id","Selektor_"+id_nummer);
obj.setAttribute("name","Selektor");
obj.setAttribute("size","1");

var obj_opt_1 = document.createElement("option");
obj_opt_1.setAttribute("value","1");
if(1==selected)
    posopval_opt_1.setAttribute("selected","selected");
obj_opt_1.innerHTML="Blah blah";
obj.appendChild(obj_opt_1);

var obj_opt_2 = document.createElement("option");
obj_opt_2.setAttribute("value","2");
if(2==selected)
    obj_opt_2.setAttribute("selected","selected");
obj_opt_2.innerHTML="2nd Row";
obj.appendChild(obj_opt_2);

...

return obj;
}

Et appelez la fonction dans le code normal:

myDiv.getObjSelektor(getObjSelektor(anotherObject.ID));

Comme je l'ai dit, c'est une approche différente qui a résolu mon problème pour mes besoins.

0
Qohelet
//
// creates 'clone' method on context object
//
//  var 
//     clon = Object.clone( anyValue );
//
!((function (propertyName, definition) {
    this[propertyName] = definition();
}).call(
    Object,
    "clone",
    function () {
        function isfn(fn) {
            return typeof fn === "function";
        }

        function isobj(o) {
            return o === Object(o);
        }

        function isarray(o) {
            return Object.prototype.toString.call(o) === "[object Array]";
        }

        function fnclon(fn) {
            return function () {
                fn.apply(this, arguments);
            };
        }

        function owns(obj, p) {
            return obj.hasOwnProperty(p);
        }

        function isemptyobj(obj) {
            for (var p in obj) {
                return false;
            }
            return true;
        }

        function isObject(o) {
            return Object.prototype.toString.call(o) === "[object Object]";
        }
        return function (input) {
            if (isfn(input)) {
                return fnclon(input);
            } else if (isobj(input)) {
                var cloned = {};
                for (var p in input) {
                    owns(Object.prototype, p)
                    || (
                        isfn(input[p])
                        && ( cloned[p] = function () { return input[p].apply(input, arguments); } )
                        || ( cloned[p] = input[p] )
                    );
                }
                if (isarray(input)) {
                    cloned.length = input.length;
                    "concat every filter forEach indexOf join lastIndexOf map pop Push reduce reduceRight reverse shift slice some sort splice toLocaleString toString unshift"
                    .split(" ")
                    .forEach(
                      function (methodName) {
                        isfn( Array.prototype[methodName] )
                        && (
                            cloned[methodName] =
                            function () {
                                return Array.prototype[methodName].apply(cloned, arguments);
                            }
                        );
                      }
                    );
                }
                return isemptyobj(cloned)
                       ? (
                          isObject(input)
                          ? cloned
                          : input
                        )
                       : cloned;
            } else {
                return input;
            }
        };
    }
));
//
0
public override

Facile

var restore = { name:'charlesi',
age:9}
var prev_data ={
name: 'charles'
age : 10
}

var temp = JSON.stringify(prev_data)
restore = JSON.parse(temp)

restore = {
name:'charlie',
age : 12}

sortie prev_data:

{
name: 'charles'
age : 10
} 
0
Louis Christopher

De la Directives de codage JavaScript Apple :

// Create an inner object with a variable x whose default
// value is 3.
function innerObj()
{
        this.x = 3;
}
innerObj.prototype.clone = function() {
    var temp = new innerObj();
    for (myvar in this) {
        // this object does not contain any objects, so
        // use the lightweight copy code.
        temp[myvar] = this[myvar];
    }
    return temp;
}

// Create an outer object with a variable y whose default
// value is 77.
function outerObj()
{
        // The outer object contains an inner object.  Allocate it here.
        this.inner = new innerObj();
        this.y = 77;
}
outerObj.prototype.clone = function() {
    var temp = new outerObj();
    for (myvar in this) {
        if (this[myvar].clone) {
            // This variable contains an object with a
            // clone operator.  Call it to create a copy.
            temp[myvar] = this[myvar].clone();
        } else {
            // This variable contains a scalar value,
            // a string value, or an object with no
            // clone function.  Assign it directly.
            temp[myvar] = this[myvar];
        }
    }
    return temp;
}

// Allocate an outer object and assign non-default values to variables in
// both the outer and inner objects.
outer = new outerObj;
outer.inner.x = 4;
outer.y = 16;

// Clone the outer object (which, in turn, clones the inner object).
newouter = outer.clone();

// Verify that both values were copied.
alert('inner x is '+newouter.inner.x); // prints 4
alert('y is '+newouter.y); // prints 16

Steve

0
Steve Harrison

Juste comme ce lien dit utiliser ce code:
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

0
Pouria Moosavi

Si votre objet est une classe (par exemple: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes ):

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

Puis, dans copiedObject, vous avez une instance profondément copiée de la classe originalObject avec toutes ses méthodes.

0
j rdl

Si vous utilisez TypeScript, devez prendre en charge les anciens navigateurs Web (et ne pouvez donc pas utiliser Object.assign), et n'utilisez pas de bibliothèque avec une méthode de duplication intégrée, vous pouvez créer vous-même un combine assistant dans quelques lignes de code. Il combine des objets, et si vous n'en avez qu'un, clonez-le simplement.

/** Creates a new object that combines the properties of the specified objects. */
function combine(...objs: {}[]) {
    const combined = {};
    objs.forEach(o => Object.keys(o).forEach(p => combined[p] = o[p]));
    return combined;
}
0
Edward Brey

Je réponds à cette question car je ne vois aucune implémentation récursive native permettant de résoudre le problème des éléments DOM.

Le problème est que <element> a les attributs parent et child, qui sont liés aux autres éléments avec les valeurs parent et child, qui renvoient à l'original <element>, provoquant soit infini récursif ou redondance cyclique.

Si votre objet est quelque chose de simple et sûr comme

{
    '123':456
}

... alors toute autre réponse ici fonctionnera probablement.

Mais si vous avez ...

{
    '123':<reactJSComponent>,
    '456':document.createElement('div'),
}

... alors vous avez besoin de quelque chose comme ça:

    // cloneVariable() : Clone variable, return null for elements or components.
var cloneVariable = function (args) {
    const variable = args.variable;

    if(variable === null) {
            return null;
    }

    if(typeof(variable) === 'object') {
            if(variable instanceof HTMLElement || variable.nodeType > 0) {
                    return null;
            }

            if(Array.isArray(variable)) {
                    var arrayclone = [];

                    variable.forEach((element) => {
                            arrayclone.Push(cloneVariable({'variable':element}));
                    });

                    return arrayclone;
            }

            var objectclone = {};

            Object.keys(variable).forEach((field) => {
                    objectclone[field] = cloneVariable({'variable':variable[field]});
            });

            return objectclone;
    }

    return variable;
}
0
HoldOffHunger