it-swarm-eu.dev

Jaký je nejúčinnější způsob hlubokého klonování objektu v JavaScriptu?

Jaký je nejúčinnější způsob klonování objektu JavaScript? Viděl jsem obj = eval(uneval(o)); je používán, ale to je nestandardní a podporováno pouze Firefoxem .

Udělal jsem věci jako obj = JSON.parse(JSON.stringify(o));, ale zpochybňuji účinnost. 

Také jsem viděl rekurzivní kopírovací funkce s různými chybami.
Jsem překvapen, že neexistuje kanonické řešení.

4863
jschrab

Poznámka: Toto je odpověď na jinou odpověď, ne na správnou odpověď na tuto otázku. Pokud chcete mít rychlé klonování objektů, postupujte podle Corbanovy rady v jejich odpovědi na tuto otázku.


Chci poznamenat, že .clone() metoda v jQuery pouze klonuje DOM elementy. Chcete-li klonovat objekty JavaScriptu, postupujte takto:

// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

Více informací naleznete v dokumentaci jQuery .

Chci také poznamenat, že hluboká kopie je vlastně mnohem chytřejší než to, co je uvedeno nahoře - je možné se vyhnout mnoha pastím (například snaze o hluboké rozšíření prvku DOM). Používá se často v jQuery jádru a v pluginech k velkému efektu.

4217
John Resig

Za předpokladu, že máte ve svém objektu pouze proměnné a ne všechny funkce, stačí použít:

var newObject = JSON.parse(JSON.stringify(oldObject));
434
Sultan Shakir

Strukturované klonování

Standard HTML obsahuje interní strukturovaný klonovací/serializační algoritmus který může vytvářet hluboké klony objektů. To je ještě omezené na jisté vestavěné typy, ale kromě nemnoho typů podporovaných JSON to také podporuje Dates, RegExps, mapy, sady, Blobs, souborové seznamy, ImageDatas, řídké pole, Typed arrays, a pravděpodobně více v budoucnosti. . Rovněž zachovává odkazy v klonovaných datech, což jí umožňuje podporovat cyklické a rekurzivní struktury, které by způsobily chyby pro JSON.

Podpora v Node.js: Experimentální ????

Modul v8 v Node.js v současné době (jako v uzlu 11) vystavuje strukturované rozhraní serializace API přímo , ale tato funkce je stále označena jako "experimentální" a podléhá změnám nebo odebrání v budoucích verzích. Pokud používáte kompatibilní verzi, je klonování objektu tak jednoduché jako:

const v8 = require('v8');

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

Přímá podpora v prohlížečích: Možná nakonec? ????

Prohlížeče v současné době neposkytují přímé rozhraní pro strukturovaný klonovací algoritmus, ale globální funkce structuredClone() byla popsána v whatwg/html # 793 na GitHub . Jak je v současné době navrženo, jeho použití pro většinu účelů by bylo stejně jednoduché jako:

const clone = structuredClone(original);

Pokud to není dodáno, implementace strukturovaných klonů prohlížečů jsou vystaveny pouze nepřímo.

Asynchronní řešení: Použitelné. ????

Nižší režijní způsob, jak vytvořit strukturovaný klon s existujícími rozhraními API, je zaúčtovat data prostřednictvím jednoho portu MessageChannels . Druhý port vydá událost message se strukturovaným klonem připojeného .data. Naslouchání těmto událostem je nutně asynchronní a synchronní alternativy jsou méně praktické.

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

Příklad použití:

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();

Synchronní řešení: Hrozné! ????

Neexistují žádné dobré možnosti pro synchronní vytváření strukturovaných klonů. Zde je pár nepraktických hacků.

history.pushState() a history.replaceState() oba vytvoří strukturovaný klon svého prvního argumentu a přiřadí tuto hodnotu history.state. Můžete to použít k vytvoření strukturovaného klonování libovolného objektu, jako je tento:

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

Příklad použití:

'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();

Ačkoliv je synchronní, může to být velmi pomalé. To způsobí všechny režijní náklady spojené s manipulací historie prohlížeče. Opakované volání této metody může způsobit, že Chrome přestane reagovat.

Notification konstruktor vytvoří strukturovaný klon svých přidružených dat. Pokouší se také zobrazit oznámení prohlížeče uživateli, ale toto se tiše nezdaří, pokud jste nepožádali o povolení k oznámení. V případě, že máte svolení k jiným účelům, okamžitě ukončíme oznámení, které jsme vytvořili.

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

Příklad použití:

'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();

308
Jeremy Banks

Pokud v něm nebyl žádný, můžete zkusit:

function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}
295
ConroyP

Efektivní způsob klonování (ne hlubokého klonování) objektu do jednoho řádku kódu

Metoda Object.assign je součástí standardu ECMAScript 2015 (ES6) a dělá přesně to, co potřebujete.

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

Metoda Object.assign () se používá ke zkopírování hodnot všech vlastních hodnot z jednoho nebo více zdrojových objektů do cílového objektu.

Přečtěte si více...

Polyfill pro podporu starších prohlížečů:

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;
    }
  });
}
149
Eugene Tiurin

Kód:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
        to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
    }

    return to;
}

Test:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
        num: 234,
        text: "asdsaD"
    }
}

var clone = extend(obj);
94
Kamarey

To je to, co používám:

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}
86
Alan

Hluboká kopie podle výkonu: Hodnoceno od nejlepšího po nejhorší

  • Reassignment "=" (řetězcová pole, pouze číselná pole)
  • Výseč (pole řetězců, pole čísel - pouze)
  • Zřetězení (řetězcová pole, pole čísel - pouze)
  • Vlastní funkce: pro smyčku nebo rekurzivní kopírování
  • jQuery je $ .extend
  • JSON.parse (pole řetězců, pole čísel, pole objektů - pouze)
  • Underscore.js 's _.clone (řetězcová pole, pouze pole čísel)
  • Lo-Dash je _.cloneDeep

Deep kopírovat pole řetězců nebo čísel (jedna úroveň - žádné referenční ukazatele):

Pokud pole obsahuje čísla a řetězce - funkce jako .slice (), .concat (), .splice (), operátor přiřazení "=" a funkce klonování Underscore.js; vytvoří hlubokou kopii prvků pole.

Pokud má opětovné přiřazení nejrychlejší výkon:

var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];

A .slice () má lepší výkon než .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

var arr1 = ['a', 'b', 'c'];  // Becomes arr1 = ['a', 'b', 'c']
var arr2a = arr1.slice(0);   // Becomes arr2a = ['a', 'b', 'c'] - deep copy
var arr2b = arr1.concat();   // Becomes arr2b = ['a', 'b', 'c'] - deep copy

Deep kopíruje pole objektů (dvě nebo více úrovní - referenční ukazatele):

var arr1 = [{object:'a'}, {object:'b'}];

Napište vlastní funkci (má rychlejší výkon než $ .extend () nebo JSON.parse):

function copy(o) {
   var out, v, key;
   out = Array.isArray(o) ? [] : {};
   for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
   }
   return out;
}

copy(arr1);

Použijte funkce obslužného programu třetích stran:

$.extend(true, [], arr1); // Jquery Extend
JSON.parse(arr1);
_.cloneDeep(arr1); // Lo-dash

Kde má $ .extend jQuery lepší výkon:

73
tfmontague
var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
60
Zibri

Vím, že je to starý post, ale myslel jsem si, že to může být nějaká pomoc další osobě, která klopýtá.

Pokud nepřidělujete objekt žádnému objektu, který nemá žádnou paměť v paměti. Chcete-li vytvořit objekt, který chcete sdílet mezi jinými objekty, musíte vytvořit takovou továrnu:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
53
Joe

Cloning objekt byl vždy problém v JS, ale to bylo všechno před ES6, já seznam různých způsobů kopírování objektu v JavaScriptu níže, představte si, že máte níže uvedený objekt a chcete mít hlubokou kopii, že:

var obj = {a:1, b:2, c:3, d:4};

Existuje několik způsobů, jak tento objekt zkopírovat, aniž by došlo ke změně původu:

1) ES5 +, pomocí jednoduché funkce pro kopírování pro vás:

function deepCopyObj(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }
    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 this object.");
}

2) ES5 +, pomocí JSON.parse a JSON.stringify.

var  deepCopyObj = JSON.parse(JSON.stringify(obj));

3) AngularJs: 

var  deepCopyObj = angular.copy(obj);

4) jQuery: 

var deepCopyObj = jQuery.extend(true, {}, obj);

5) UnderscoreJs & Loadash: 

var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy

Doufám, že tato pomoc ... 

52
Alireza

Existuje knihovna (nazývaná „klon“) , která to dělá docela dobře. Poskytuje nejkomplexnější rekurzivní klonování/kopírování libovolných objektů, o kterých vím. Podporuje také kruhové odkazy, které ještě nejsou uvedeny v ostatních odpovědích.

Můžete také najít na npm také. Může být použit pro prohlížeč i Node.js.

Zde je příklad, jak jej používat:

Nainstalujte ho pomocí

npm install clone

nebo jej zabalte s Ender .

ender build clone [...]

Zdrojový kód můžete také stáhnout ručně.

Pak jej můžete použít ve zdrojovém kódu.

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(Zřeknutí se odpovědnosti: Jsem autorem knihovny.)

52
pvorb

Pokud jej používáte, má knihovna Underscore.js metodu klon .

var newObject = _.clone(oldObject);
48
itsadok

Zde je verze odpovědi společnosti ConroyP, která funguje, i když má konstruktor požadované parametry:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

Tato funkce je k dispozici také v knihovně my simpleoo .

Upravit:

Zde je robustnější verze (díky Justin McCandlessu nyní podporuje i cyklické odkazy):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.Push(src);
        _copiesVisited.Push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.Push(src);
    _copiesVisited.Push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}
36
Matt Browne

Následující vytvoří dvě instance stejného objektu. Našel jsem ho a používám ho v současné době. Je to jednoduché a snadno použitelné.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));
31
nathan rogers

Hluboké kopírování objektů v JavaScriptu (myslím, že nejlepší a nejjednodušší)

1. Použití JSON.parse (JSON.stringify (object));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2.Použití vytvořené metody

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Použití Lo-Dash's _.cloneDeep link lodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Použití metody Object.assign ()

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

ALE VELKÉ KDYŽ

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5. Použití Underscore.js _.clone link Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

ALE VELKÉ KDYŽ

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

Reference medium.com

JSBEN.CH Výkon Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd Performance Deep copying objects in JavaScript

28
TinhNQ

Lodash má hodnotu Nice _.cloneDeep (value) :

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
23
opensas

Crockford navrhuje (a dávám přednost) pomocí této funkce:

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

var newObject = object(oldObject);

Je to napjaté, funguje podle očekávání a nepotřebujete knihovnu.


UPRAVIT:

Toto je polyfill pro Object.create, takže to můžete také použít.

var newObject = Object.create(oldObject);

POZNÁMKA: Pokud použijete některou z těchto možností, můžete mít problémy s iterací, která používá hasOwnProperty. Protože create vytvořit nový prázdný objekt, který dědí oldObject. Pro klonování objektů je však stále užitečné a praktické.

Například, pokud oldObject.a = 5;

newObject.a; // is 5

ale:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
23
Chris Broski
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
22
Mark Cidade

Mělká kopie jedné vložky ( ECMAScript 5. vydání ):

var Origin = { foo : {} };
var copy = Object.keys(Origin).reduce(function(c,k){c[k]=Origin[k];return c;},{});

console.log(Origin, copy);
console.log(Origin == copy); // false
console.log(Origin.foo == copy.foo); // true

A mělké kopírování ( ECMAScript 6. vydání , 2015):

var Origin = { foo : {} };
var copy = Object.assign({}, Origin);

console.log(Origin, copy);
console.log(Origin == copy); // false
console.log(Origin.foo == copy.foo); // true
20
Maël Nison

Jen proto, že jsem neviděl AngularJS zmínil se a myslel si, že by lidé mohli chtít vědět ...

angular.copy také poskytuje metodu hlubokého kopírování objektů a polí.

17
Dan Atkinson

Zdá se, že neexistuje žádný ideální operátor hlubokého klonování pro objekty typu pole. Jak ukazuje níže uvedený kód, klonovač jQuery společnosti John Resig změní pole s nečíselnými vlastnostmi na objekty, které nejsou maticemi, a klonovač RegSww's JSON odloží nečíselné vlastnosti. Následující body ilustrují tyto body ve více prohlížečích:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)
16
Page Notes

Mám dvě dobré odpovědi v závislosti na tom, zda vaším cílem je klonovat "prostý starý JavaScript objekt" nebo ne.

Předpokládejme také, že vaším záměrem je vytvořit kompletní klon bez odkazů na prototyp zpět ke zdrojovému objektu. Pokud vás nezajímá kompletní klon, můžete použít mnoho rutin Object.clone () uvedených v některých dalších odpovědích (Crockfordův vzor).

Pro obyčejné staré objekty JavaScriptu, vyzkoušený a pravdivý způsob, jak klonovat objekt v moderních runtimech, je jednoduše:

var clone = JSON.parse(JSON.stringify(obj));

Zdrojový objekt musí být čistý objekt JSON. To znamená, že všechny jeho vnořené vlastnosti musí být skalární (jako boolean, string, array, object, atd.). Jakékoli funkce nebo speciální objekty jako RegExp nebo Date nebudou klonovány.

Je to efektivní? Sakra ano. Vyzkoušeli jsme všechny druhy klonovacích metod a to funguje nejlépe. Jsem si jist, že ninja by mohla vykouzlit rychlejší metodu. Ale mám podezření, že mluvíme o marginálních ziscích.

Tento přístup je jednoduchý a snadno realizovatelný. Zabalte ji do funkce pohodlí a pokud opravdu potřebujete vytlačit nějaký zisk, pokračujte později.

Nyní, pro non-plain JavaScript objekty, není opravdu jednoduchá odpověď. Ve skutečnosti nemůže existovat kvůli dynamické povaze funkcí JavaScriptu a stavu vnitřního objektu. Hluboké klonování struktury JSON s funkcemi uvnitř vyžaduje, abyste tyto funkce a jejich vnitřní kontext znovu vytvořili. A JavaScript prostě nemá standardizovaný způsob, jak toho dosáhnout.

Správný způsob, jak to provést, je opět pomocí metody, kterou deklarujete a znovu použijete ve svém kódu. Komfortní metoda může být obdařena nějakým porozuměním vlastním objektům, takže se můžete ujistit, že graf znovu vytvoříte v novém objektu.

Jsme napsáni sami, ale nejlepší obecný přístup, který jsem viděl, je uveden zde:

http://davidwalsh.name/javascript-clone

To je správný nápad. Autor (David Walsh) komentoval klonování zobecněných funkcí. V závislosti na vašem případu použití se můžete rozhodnout.

Hlavní myšlenka spočívá v tom, že je třeba speciálně zpracovat konkretizaci vašich funkcí (nebo prototypových tříd, abych tak řekl) na základě typu. Zde je několik příkladů pro RegExp a Date.

Nejen, že je tento kód stručný, ale je také velmi čitelný. Je to docela snadné rozšířit.

Je to efektivní? Sakra ano. Vzhledem k tomu, že cílem je vytvořit skutečný klon s hlubokými kopiemi, budete muset projít členy grafu zdrojového objektu. S tímto přístupem můžete Tweak přesně určit, které dětské členy mají léčit a jak ručně zpracovávat vlastní typy.

Tak tam jdete. Dva přístupy. Oba jsou podle mého názoru efektivní.

15

To není obecně nejúčinnější řešení, ale dělá to, co potřebuji. Jednoduché testovací případy níže ...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

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

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.Push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

Test cyklického pole ...

a = []
a.Push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

Funkční test...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
13
neatonk

AngularJS

Pokud používáte hranaté, můžete to udělat taky

var newObject = angular.copy(oldObject);
11
azerafati

Nesouhlasím s odpovědí s největšími hlasy zde . A Rekurzivní Deep Clone je mnohem rychlejší než uvedený přístup JSON.parse (JSON.stringify (obj))

A zde je funkce pro rychlou orientaci:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}
10
prograhammer
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};
10
Dima

Pro lidi, kteří chtějí používat verzi JSON.parse(JSON.stringify(obj)), ale aniž by ztratili objekty Date, můžete použít druhý argument metody parse pro převod řetězců zpět na Date:

function clone(obj) {
  var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
  return JSON.parse(JSON.stringify(x), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v);
    return v;
  });
}
8
Buzinas

Zde je komplexní metoda klonování (), která může klonovat jakýkoli objekt JavaScriptu. Zvládá téměř všechny případy:

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;
};
8
user1547016

Obvykle používám var newObj = JSON.parse( JSON.stringify(oldObje) );, ale tady je správnější způsob:

var o = {};

var oo = Object.create(o);

(o === oo); // => false

Sledujte starší prohlížeče!

6
Cody

Používám klonovou knihovnu npm. Zřejmě to také funguje v prohlížeči.

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

let a = clone(b)
6
user3071643

Pouze když můžete použít ECMAScript 6 nebo transpilers .

Funkce:

  • Nebude spouštět getter/setter při kopírování.
  • Zachovává getter/setter.
  • Zachovává informace o prototypu.
  • Pracuje s oběma object-literal a funkčními funkcemiOO /.

Kód:

function clone(target, source){

    for(let key in source){

        // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
        let descriptor = Object.getOwnPropertyDescriptor(source, key);
        if(descriptor.value instanceof String){
            target[key] = new String(descriptor.value);
        }
        else if(descriptor.value instanceof Array){
            target[key] = clone([], descriptor.value);
        }
        else if(descriptor.value instanceof Object){
            let prototype = Reflect.getPrototypeOf(descriptor.value);
            let cloneObject = clone({}, descriptor.value);
            Reflect.setPrototypeOf(cloneObject, prototype);
            target[key] = cloneObject;
        }
        else {
            Object.defineProperty(target, key, descriptor);
        }
    }
    let prototype = Reflect.getPrototypeOf(source);
    Reflect.setPrototypeOf(target, prototype);
    return target;
}
6
andrew

Klonování objektu pomocí dnešního JavaScriptu: ECMAScript 2015 (dříve známý jako ECMAScript 6)

var original = {a: 1};

// Method 1: New object with original assigned.
var copy1 = Object.assign({}, original);

// Method 2: New object with spread operator assignment.
var copy2 = {...original};

Staré prohlížeče nemusí podporovat ECMAScript 2015. Společným řešením je použít kompilátor JavaScript-to-JavaScript jako Babel k výstupu ECMAScript 5 verze vašeho JavaScript kódu.

Jako zdůraznil @ jim-hall , toto je jen mělká kopie. Vlastnosti vlastností se zkopírují jako reference: změna vlastnosti by změnila hodnotu v jiném objektu/instanci.

6
Barry Staes

Jednořádkové řešení ECMAScript 6 (speciální typy objektů jako Date/Regex nejsou zpracovány):

const clone = (o) =>
  typeof o === 'object' && o !== null ?      // only clone objects
  (Array.isArray(o) ?                        // if cloning an array
    o.map(e => clone(e)) :                   // clone each of its elements
    Object.keys(o).reduce(                   // otherwise reduce every key in the object
      (r, k) => (r[k] = clone(o[k]), r), {}  // and save its cloned value into a new object
    )
  ) :
  o;                                         // return non-objects as is

var x = {
  nested: {
    name: 'test'
  }
};

var y = clone(x);

console.log(x.nested !== y.nested);

5
Shishir Arora

Na tuto otázku jsem pozdě, ale mám jiný způsob klonování objektu:

   function cloneObject(obj) {
        if (obj === null || typeof(obj) !== 'object')
            return obj;
        var temp = obj.constructor(); // changed
        for (var key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                obj['isActiveClone'] = null;
                temp[key] = cloneObject(obj[key]);
                delete obj['isActiveClone'];
            }
        }
        return temp;
    }



var b = cloneObject({"a":1,"b":2});   // calling

který je mnohem lepší a rychlejší než:

var a = {"a":1,"b":2};
var b = JSON.parse(JSON.stringify(a));  

a

var a = {"a":1,"b":2};

// Deep copy
var newObject = jQuery.extend(true, {}, a);

Mám kód označený lavičkou a výsledky můžete otestovat zde :

a sdílení výsledků:  enter image description here Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

5
Mayur Agarwal

Lodash má funkci, která se vám to líbí.

var foo = {a: 'a', b: {c:'d', e: {f: 'g'}}};

var bar = _.cloneDeep(foo);
// bar = {a: 'a', b: {c:'d', e: {f: 'g'}}} 

Přečtěte si dokumenty zde .

5
Daniel Barde

Toto je nejrychlejší metoda, kterou jsem vytvořil a která prototyp nepoužívá, takže bude v novém objektu zachovat vlastnost hasOwnProperty.

Řešením je iterovat vlastnosti nejvyšší úrovně původního objektu, vytvořit dvě kopie, odstranit každou vlastnost z originálu a obnovit původní objekt a vrátit novou kopii. Musí pouze iterovat tolikrát, kolik je vlastností nejvyšší úrovně. Tím se uloží všechny podmínky if a zkontroluje se, zda je každá vlastnost funkcí, objektem, řetězcem atd. A nemusí iterovat každou vlastnost potomka.

Jedinou nevýhodou je, že původní objekt musí být dodán s původním vytvořeným oborem názvů, aby jej bylo možné obnovit.

copyDeleteAndReset:function(namespace,strObjName){
    var obj = namespace[strObjName],
    objNew = {},objOrig = {};
    for(i in obj){
        if(obj.hasOwnProperty(i)){
            objNew[i] = objOrig[i] = obj[i];
            delete obj[i];
        }
    }
    namespace[strObjName] = objOrig;
    return objNew;
}

var namespace = {};
namespace.objOrig = {
    '0':{
        innerObj:{a:0,b:1,c:2}
    }
}

var objNew = copyDeleteAndReset(namespace,'objOrig');
objNew['0'] = 'NEW VALUE';

console.log(objNew['0']) === 'NEW VALUE';
console.log(namespace.objOrig['0']) === innerObj:{a:0,b:1,c:2};
4
Steve Tomlin

Současný návrh ECMAScript 6 introduces Object.assign jako způsob klonování objektů. Příklad kódu bude:

var obj1 = { a: true, b: 1 };
var obj2 = Object.assign(obj1);
console.log(obj2); // { a: true, b: 1 }

V době psaní podpora je omezena na Firefox 34 v prohlížečích tak to ještě není použitelné v produkčním kódu (pokud nepíšete rozšíření Firefoxu samozřejmě).

4
Robin Whittleton

Příklad ES 2017:

let objectToCopy = someObj;
let copyOfObject = {};
Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(objectToCopy));
// copyOfObject will now be the same as objectToCopy
4
codeMonkey

Co asynchronní klonování objektů provedené Promise?

async function clone(thingy /**/)
{
    if(thingy instanceof Promise)
    {
        throw Error("This function cannot clone Promises.");
    }
    return thingy;
}

V JavaScriptu můžete napsat svou metodu deepCopy 

function deepCopy(src) {
  let target = Array.isArray(src) ? [] : {};
  for (let prop in src) {
    let value = src[prop];
    if(value && typeof value === 'object') {
      target[prop] = deepCopy(value);
  } else {
      target[prop] = value;
  }
 }
    return target;
}
3
chandan gupta

Existuje mnoho způsobů, jak toho dosáhnout, ale pokud to chcete udělat bez jakékoli knihovny, můžete použít následující:

const cloneObject = (oldObject) => {
  let newObject = oldObject;
  if (oldObject && typeof oldObject === 'object') {
    if(Array.isArray(oldObject)) {
      newObject = [];
    } else if (Object.prototype.toString.call(oldObject) === '[object Date]' && !isNaN(oldObject)) {
      newObject = new Date(oldObject.getTime());
    } else {
      newObject = {};
      for (let i in oldObject) {
        newObject[i] = cloneObject(oldObject[i]);
      }
    }

  }
  return newObject;
}

Dej mi vědět, co si myslíš.

3
shobhit1

Toto je moje verze objektu cloner. Jedná se o samostatnou verzi metody jQuery, která obsahuje pouze několik vylepšení a úprav. Podívejte se na fiddle . Použil jsem spoustu jQuery až do dne, kdy jsem si uvědomil, že tuto funkci používám většinu času x_x.

Použití je stejné, jak je popsáno v API jQuery:

  • Non-deep klon: extend(object_dest, object_source);
  • Hluboký klon: extend(true, object_dest, object_source);

Jedna další funkce slouží k definování, zda má být objekt klonován.

/**
 * This is a quasi clone of jQuery's extend() function.
 * by Romain WEEGER for wJs library - www.wexample.com
 * @returns {*|{}}
 */
function extend() {
    // Make a copy of arguments to avoid JavaScript inspector hints.
    var to_add, name, copy_is_array, clone,

    // The target object who receive parameters
    // form other objects.
    target = arguments[0] || {},

    // Index of first argument to mix to target.
    i = 1,

    // Mix target with all function arguments.
    length = arguments.length,

    // Define if we merge object recursively.
    deep = false;

    // Handle a deep copy situation.
    if (typeof target === 'boolean') {
        deep = target;

        // Skip the boolean and the target.
        target = arguments[ i ] || {};

        // Use next object as first added.
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if (typeof target !== 'object' && typeof target !== 'function') {
        target = {};
    }

    // Loop trough arguments.
    for (false; i < length; i += 1) {

        // Only deal with non-null/undefined values
        if ((to_add = arguments[ i ]) !== null) {

            // Extend the base object.
            for (name in to_add) {

                // We do not wrap for loop into hasOwnProperty,
                // to access to all values of object.
                // Prevent never-ending loop.
                if (target === to_add[name]) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays.
                if (deep && to_add[name] && (is_plain_object(to_add[name]) || (copy_is_array = Array.isArray(to_add[name])))) {
                    if (copy_is_array) {
                        copy_is_array = false;
                        clone = target[name] && Array.isArray(target[name]) ? target[name] : [];
                    }
                    else {
                        clone = target[name] && is_plain_object(target[name]) ? target[name] : {};
                    }

                    // Never move original objects, clone them.
                    target[name] = extend(deep, clone, to_add[name]);
                }

                // Don't bring in undefined values.
                else if (to_add[name] !== undefined) {
                    target[name] = to_add[name];
                }
            }
        }
    }
    return target;
}

/**
 * Check to see if an object is a plain object
 * (created using "{}" or "new Object").
 * Forked from jQuery.
 * @param obj
 * @returns {boolean}
 */
function is_plain_object(obj) {
    // Not plain objects:
    // - Any object or value whose internal [[Class]] property is not "[object Object]"
    // - DOM nodes
    // - window
    if (obj === null || typeof obj !== "object" || obj.nodeType || (obj !== null && obj === obj.window)) {
        return false;
    }
    // Support: Firefox <20
    // The try/catch suppresses exceptions thrown when attempting to access
    // the "constructor" property of certain Host objects, i.e. |window.location|
    // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
    try {
        if (obj.constructor && !this.hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) {
            return false;
        }
    }
    catch (e) {
        return false;
    }

    // If the function hasn't returned already, we're confident that
    // |obj| is a plain object, created by {} or constructed with new Object
    return true;
}
3
weeger

Myslím si, že je to nejlepší řešení, pokud chcete zobecnit objektový klonovací algoritmus.
Může být použit s nebo bez jQuery, i když doporučuji ponechat metodu rozšíření jQuery, pokud chcete, aby klonovaný objekt měl stejnou "třídu" jako původní.

function clone(obj){
    if(typeof(obj) == 'function')//it's a simple function
        return obj;
    //of it's not an object (but could be an array...even if in javascript arrays are objects)
    if(typeof(obj) !=  'object' || obj.constructor.toString().indexOf('Array')!=-1)
        if(JSON != undefined)//if we have the JSON obj
            try{
                return JSON.parse(JSON.stringify(obj));
            }catch(err){
                return JSON.parse('"'+JSON.stringify(obj)+'"');
            }
        else
            try{
                return eval(uneval(obj));
            }catch(err){
                return eval('"'+uneval(obj)+'"');
            }
    // I used to rely on jQuery for this, but the "extend" function returns
    //an object similar to the one cloned,
    //but that was not an instance (instanceof) of the cloned class
    /*
    if(jQuery != undefined)//if we use the jQuery plugin
        return jQuery.extend(true,{},obj);
    else//we recursivley clone the object
    */
    return (function _clone(obj){
        if(obj == null || typeof(obj) != 'object')
            return obj;
        function temp () {};
        temp.prototype = obj;
        var F = new temp;
        for(var key in obj)
            F[key] = clone(obj[key]);
        return F;
    })(obj);            
}
3
gion_13

Existuje mnoho odpovědí, ale žádný z nich nevyžadoval požadovaný efekt, který jsem potřeboval. Chtěl jsem využít sílu hluboké kopie jQuery ... Nicméně, když to běží do pole, to prostě zkopíruje odkaz na pole a hluboké kopie položky v něm. Chcete-li se dostat kolem tohoto, jsem udělal Nice malý rekurzivní funkce, která vytvoří nové pole automaticky. 

(Dokonce kontroluje, zda chcete kendo.data.ObservableArray, pokud chcete! I když se ujistěte, že se ujistíte, že zavoláte kendo.observable (newItem), pokud chcete, aby pole bylo opět pozorovatelné.) 

Chcete-li tedy plně zkopírovat existující položku, postupujte takto:

var newItem = jQuery.extend(true, {}, oldItem);
createNewArrays(newItem);


function createNewArrays(obj) {
    for (var prop in obj) {
        if ((kendo != null && obj[prop] instanceof kendo.data.ObservableArray) || obj[prop] instanceof Array) {
            var copy = [];
            $.each(obj[prop], function (i, item) {
                var newChild = $.extend(true, {}, item);
                createNewArrays(newChild);
                copy.Push(newChild);
            });
            obj[prop] = copy;
        }
    }
}
3
Daniel Lorenz

Zde je můj způsob hlubokého klonování objektu s ES2015 výchozí hodnotou a operátorem spreadů

 const makeDeepCopy = (obj, copy = {}) => {
  for (let item in obj) {
    if (typeof obj[item] === 'object') {
      makeDeepCopy(obj[item], copy)
    }
    if (obj.hasOwnProperty(item)) {
      copy = {
        ...obj
      }
    }
  }
  return copy
}

const testObj = {
  "type": "object",
  "properties": {
    "userId": {
      "type": "string",
      "chance": "guid"
    },
    "emailAddr": {
      "type": "string",
      "chance": {
        "email": {
          "domain": "fake.com"
        }
      },
      "pattern": "[email protected]ake.com"
    }
  },
  "required": [
    "userId",
    "emailAddr"
  ]
}

const makeDeepCopy = (obj, copy = {}) => {
  for (let item in obj) {
    if (typeof obj[item] === 'object') {
      makeDeepCopy(obj[item], copy)
    }
    if (obj.hasOwnProperty(item)) {
      copy = {
        ...obj
      }
    }
  }
  return copy
}

console.log(makeDeepCopy(testObj))

3
Julez

Bez dotyku prototypického dědictví můžete hluboko osamocené objekty a pole následovně;

function objectClone(o){
  var ot = Array.isArray(o);
  return o !== null && typeof o === "object" ? Object.keys(o)
                                                     .reduce((r,k) => o[k] !== null && typeof o[k] === "object" ? (r[k] = objectClone(o[k]),r)
                                                                                                                : (r[k] = o[k],r), ot ? [] : {})
                                             : o;
}
var obj = {a: 1, b: {c: 2, d: {e: 3, f: {g: 4, h: null}}}},
    arr = [1,2,[3,4,[5,6,[7]]]],
    nil = null,
  clobj = objectClone(obj),
  clarr = objectClone(arr),
  clnil = objectClone(nil);
console.log(clobj, obj === clobj);
console.log(clarr, arr === clarr);
console.log(clnil, nil === clnil);
clarr[2][2][2] = "seven";
console.log(arr, clarr);

2
Redu

Pro mělkou kopii je ve standardu ECMAScript2018 zavedena velká, jednoduchá metoda. Jedná se o použití Spread Operator :

let obj = {a : "foo", b:"bar" , c:10 , d:true , e:[1,2,3] };

let objClone = { ...obj };

Testoval jsem to v prohlížeči Chrome, oba objekty jsou uloženy na různých místech, takže změna okamžitých hodnot dítěte v obou se nezmění. I když (v příkladu) změna hodnoty v e ovlivní obě kopie.

Tato technika je velmi jednoduchá a přímočará. Považuji to za nejlepší zkušenost pro tuto otázku jednou provždy. 

2
Vikram K

Použijte Object.create() k získání prototype a podpory pro instanceof a použijte smyčku for() k získání číselných klíčů:

function cloneObject(source) {
    var key,value;
    var clone = Object.create(source);

    for (key in source) {
        if (source.hasOwnProperty(key) === true) {
            value = source[key];

            if (value!==null && typeof value==="object") {
                clone[key] = cloneObject(value);
            } else {
                clone[key] = value;
            }
        }
    }

    return clone;
}
2
Steven Vachon

class Handler {
  static deepCopy (obj) {
    if (Object.prototype.toString.call(obj) === '[object Array]') {
      const result = [];
      
      for (let i = 0, len = obj.length; i < len; i++) {
        result[i] = Handler.deepCopy(obj[i]);
      }
      return result;
    } else if (Object.prototype.toString.call(obj) === '[object Object]') {
      const result = {};
      for (let prop in obj) {
        result[prop] = Handler.deepCopy(obj[prop]);
      }
      return result;
    }
    return obj;
  }
}

2
Ihor Pavlyk

Vyžaduje nové prohlížeče, ale ...

Pojďme rozšířit nativní objekt a dostat real .extend();

Object.defineProperty(Object.prototype, 'extend', {
    enumerable: false,
    value: function(){
        var that = this;

        Array.prototype.slice.call(arguments).map(function(source){
            var props = Object.getOwnPropertyNames(source),
                i = 0, l = props.length,
                prop;

            for(; i < l; ++i){
                prop = props[i];

                if(that.hasOwnProperty(prop) && typeof(that[prop]) === 'object'){
                    that[prop] = that[prop].extend(source[prop]);
                }else{
                    Object.defineProperty(that, prop, Object.getOwnPropertyDescriptor(source, prop));
                }
            }
        });

        return this;
    }
});

Jen pop, že v před kód, který používá .extend () na objektu.

Příklad:

var obj1 = {
    node1: '1',
    node2: '2',
    node3: 3
};

var obj2 = {
    node1: '4',
    node2: 5,
    node3: '6'
};

var obj3 = ({}).extend(obj1, obj2);

console.log(obj3);
// Object {node1: "4", node2: 5, node3: "6"}
2
Tristian

V roce 2019 používám:

deepCopy(object) {
  const getCircularReplacer = () => {
    const seen = new WeakSet();
    return (key, value) => {
      if(typeof value === 'object' && value !== null) {
        if(seen.has(value)) return;
        seen.add(value);
      }
      return value;
    };
  };
  return JSON.parse(JSON.stringify(object, getCircularReplacer()));
}

const theCopy = deepCopy(originalObject);

2

Protože rekurze je pro JavaScript příliš drahá a většina odpovědí, které jsem našel, používá rekurzi, zatímco přístup JSON přeskočí části, které nelze převést na JSON (funkce atd.). Tak jsem udělal malý výzkum a zjistil, že tato technika trampolíny se jí vyhnula. Zde je kód:

/*
 * Trampoline to avoid recursion in JavaScript, see:
 *     http://www.integralist.co.uk/posts/js-recursion.html
 */
function trampoline() {
    var func = arguments[0];
    var args = [];
    for (var i = 1; i < arguments.length; i++) {
        args[i - 1] = arguments[i];
    }

    var currentBatch = func.apply(this, args);
    var nextBatch = [];

    while (currentBatch && currentBatch.length > 0) {
        currentBatch.forEach(function(eachFunc) {
            var ret = eachFunc();
            if (ret && ret.length > 0) {
                nextBatch = nextBatch.concat(ret);
            }
        });

        currentBatch = nextBatch;
        nextBatch = [];
    }
};

/*
 *  Deep clone an object using the trampoline technique.
 *
 *  @param target {Object} Object to clone
 *  @return {Object} Cloned object.
 */
function clone(target) {
    if (typeof target !== 'object') {
        return target;
    }
    if (target == null || Object.keys(target).length == 0) {
        return target;
    }

    function _clone(b, a) {
        var nextBatch = [];
        for (var key in b) {
            if (typeof b[key] === 'object' && b[key] !== null) {
                if (b[key] instanceof Array) {
                    a[key] = [];
                }
                else {
                    a[key] = {};
                }
                nextBatch.Push(_clone.bind(null, b[key], a[key]));
            }
            else {
                a[key] = b[key];
            }
        }
        return nextBatch;
    };

    var ret = target instanceof Array ? [] : {};
    (trampoline.bind(null, _clone))(target, ret);
    return ret;
};

Viz také tento Gist: https://Gist.github.com/SeanOceanHu/7594cafbfab682f790eb

2
Bodhi Hu

Pro budoucí použití lze tento kód použít

ES6:

_clone: function(obj){
    let newObj = {};
    for(let i in obj){
        if(typeof(obj[i]) === 'object' && Object.keys(obj[i]).length){
            newObj[i] = clone(obj[i]);
        } else{
            newObj[i] = obj[i];
        }
    }
    return Object.assign({},newObj);
}

ES5:

function clone(obj){
let newObj = {};
for(let i in obj){
    if(typeof(obj[i]) === 'object' && Object.keys(obj[i]).length){
        newObj[i] = clone(obj[i]);
    } else{
        newObj[i] = obj[i];
    }
}
return Object.assign({},newObj);

}

Např 

var obj ={a:{b:1,c:3},d:4,e:{f:6}}
var xc = clone(obj);
console.log(obj); //{a:{b:1,c:3},d:4,e:{f:6}}
console.log(xc); //{a:{b:1,c:3},d:4,e:{f:6}}

xc.a.b = 90;
console.log(obj); //{a:{b:1,c:3},d:4,e:{f:6}}
console.log(xc); //{a:{b:90,c:3},d:4,e:{f:6}}
2
Ashutosh Jha

Object.assign({},sourceObj) objekt pouze klonuje, pokud jeho vlastnost nemá klíč referenčního typu. ex

obj={a:"lol",b:["yes","no","maybe"]}
clonedObj = Object.assign({},obj);

clonedObj.b.Push("skip")// changes will reflected to the actual obj as well because of its reference type.
obj.b //will also console => yes,no,maybe,skip

Tak pro hluboké klonování není možné dosáhnout tímto způsobem.

Nejlepší řešení, které funguje, je

var obj = Json.stringify(yourSourceObj)
var cloned = Json.parse(obj);
2

Podle mých zkušeností rekurzivní verze výrazně překonává JSON.parse(JSON.stringify(obj)). Zde je modernizovaná funkce rekurzivního kopírování hlubokých objektů, která se vejde na jeden řádek:

function deepCopy(obj) {
  return Object.keys(obj).reduce((v, d) => Object.assign(v, {
    [d]: (obj[d].constructor === Object) ? deepCopy(obj[d]) : obj[d]
  }), {});
}

To je asi 40 krát rychlejší než metoda JSON.parse....

2
Parabolord

Existují tři různé způsoby klonování objektů ve Javascriptu.

1: Hluboká kopie pomocí iterace

function iterationCopy(src) {
  let target = {};
  for (let prop in src) {
    if (src.hasOwnProperty(prop)) {
      target[prop] = src[prop];
    }
  }
  return target;
}
const source = {a:1, b:2, c:3};
const target = iterationCopy(source);
console.log(target); // {a:1, b:2, c:3}
// Check if clones it and not changing it
source.a = 'a';
console.log(source.a); // 'a'
console.log(target.a); // 1

Jak vidíte, funguje to!

Pojďme nyní snížit pronásledování na druhé řešení, které je skutečně elegantnější, ale omezenější na použití.

2: Převod na JSON a zpět

function jsonCopy(src) {
  return JSON.parse(JSON.stringify(src));
}
const source = {a:1, b:2, c:3};
const target = jsonCopy(source);
console.log(target); // {a:1, b:2, c:3}
// Check if clones it and not changing it
source.a = 'a';
console.log(source.a); // 'a'
console.log(target.a); // 1

Poznámka: Buďte opatrní při používání této metody, protože zdrojový objekt MUSÍ být JSON bezpečný. Může tedy vyžadovat určitý druh zpracování výjimek, aby byl bezpečný v případech, kdy zdrojový objekt není konvertibilní na JSON.

3: Použití Object.assign

Update: Tato metoda má chybu, že to dělá jen mělké kopie. Znamená to, že vnořené vlastnosti budou stále kopírovány odkazem. Buďte na to opatrní.

Tímto způsobem je to nejlepší a nejbezpečnější způsob, jak osobně konzumuji v mých projektech. Využívá zabudovanou statickou metodu na objektu Object a je ovládána a poskytována jazykem. Použijte tento!

function bestCopyEver(src) {
  return Object.assign({}, src);
}
const source = {a:1, b:2, c:3};
const target = bestCopyEver(source);
console.log(target); // {a:1, b:2, c:3}
// Check if clones it and not changing it
source.a = 'a';
console.log(source.a); // 'a'
console.log(target.a); // 1
2
Kishan Patel

Toto je řešení s rekurzí:

obj = {
  a: { b: { c: { d: ['1', '2'] } } },
  e: 'Saeid'
}
const Clone = function (obj) {
  
  const container = Array.isArray(obj) ? [] : {}
  const keys  = Object.keys(obj)
   
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    if(typeof obj[key] == 'object') {
      container[key] = Clone(obj[key])
    }
    else
      container[key] = obj[key].slice()
  }
  
  return container
}
 console.log(Clone(obj))

2
SAlidadi

pokud zjistíte, že děláte něco takového pravidelného (např. vytvoření funkce pro vrácení zpět), může to být užitečné pro nahlédnutí do Immutable.js

const map1 = Immutable.fromJS( { a: 1, b: 2, c: { d: 3 } } );
const map2 = map1.setIn( [ 'c', 'd' ], 50 );

console.log( `${ map1.getIn( [ 'c', 'd' ] ) } vs ${ map2.getIn( [ 'c', 'd' ] ) }` ); // "3 vs 50"

https://codepen.io/anon/pen/OBpqNE?editors=1111

1
shunryu111

Při pohledu přes tento dlouhý seznam odpovědí byla pokryta téměř všechna řešení s výjimkou těch, o nichž jsem si vědom. Toto je seznam Vanilla JS způsobů hlubokého klonování objektu.

  1. JSON.parse (JSON.stringify (obj));

  2. Prostřednictvím history.state s pushState nebo replaceState

  3. Webové oznámení API, ale to má nevýhodu žádat uživatele o oprávnění.

  4. Provádění vlastní rekurzivní smyčky přes objekt kopírovat každou úroveň.

  5. Odpověď, kterou jsem neviděl -> Používání servisních pracovníků. Zprávy (objekty) předané mezi stránkou a skriptem ServiceWorker budou hluboké klony libovolného objektu.

1
Steve Griffith

Vzhledem k tomu, že tato otázka má spoustu pozornosti a odpovědí s odkazem na vestavěné funkce, jako je Object.assign nebo vlastní kód pro hluboké klonování, rád bych sdílel některé knihovny k hlubokému klonování, 

1. esclone

npm install --savedev esclone https://www.npmjs.com/package/esclone

Příklad použití v ES6:

import esclone from "esclone";

const rockysGrandFather = {
  name: "Rockys grand father",
  father: "Don't know :("
};
const rockysFather = {
  name: "Rockys Father",
  father: rockysGrandFather
};

const rocky = {
  name: "Rocky",
  father: rockysFather
};

const rockyClone = esclone(rocky);

Příklad použití v ES5:

var esclone = require("esclone")
var foo = new String("abcd")
var fooClone = esclone.default(foo)
console.log(fooClone)
console.log(foo === fooClone)

2. hluboká kopie

npm nainstalovat deep-copy https://www.npmjs.com/package/deep-copy

Příklad:

var dcopy = require('deep-copy')

// deep copy object 
var copy = dcopy({a: {b: [{c: 5}]}})

// deep copy array 
var copy = dcopy([1, 2, {a: {b: 5}}])

3. hluboký klon

$ npm install --save clone-deep https://www.npmjs.com/package/clone-deep

Příklad:

var cloneDeep = require('clone-deep');

var obj = {a: 'b'};
var arr = [obj];

var copy = cloneDeep(arr);
obj.c = 'd';

console.log(copy);
//=> [{a: 'b'}] 

console.log(arr);
1
JTeam

Můj scénář byl trochu jiný. Měl jsem objekt s vnořenými objekty, stejně jako funkce. Proto Object.assign() a JSON.stringify() nebyly řešením mého problému. Použití knihoven třetích stran nebylo pro mě možností.

Proto jsem se rozhodl udělat jednoduchou funkci pro použití vestavěných metod pro kopírování objektu s jeho doslovnými vlastnostmi, vnořenými objekty a funkcemi.

let deepCopy = (target, source) => {
    Object.assign(target, source);
    // check if there's any nested objects
    Object.keys(source).forEach((prop) => {
        /**
          * assign function copies functions and
          * literals (int, strings, etc...)
          * except for objects and arrays, so:
          */
        if (typeof(source[prop]) === 'object') {
            // check if the item is, in fact, an array
            if (Array.isArray(source[prop])) {
                // clear the copied referenece of nested array
                target[prop] = Array();
                // iterate array's item and copy over
                source[prop].forEach((item, index) => {
                    // array's items could be objects too!
                    if (typeof(item) === 'object') {
                        // clear the copied referenece of nested objects
                        target[prop][index] = Object();
                        // and re do the process for nested objects
                        deepCopy(target[prop][index], item);
                    } else {
                        target[prop].Push(item);
                    }
                });
            // otherwise, treat it as an object
            } else {
                // clear the copied referenece of nested objects
                target[prop] = Object();
                // and re do the process for nested objects
                deepCopy(target[prop], source[prop]);
            }
        }
    });
};

Zde je testovací kód:

let a = {
    name: 'Human', 
    func: () => {
        console.log('Hi!');
    }, 
    prop: {
        age: 21, 
        info: {
            hasShirt: true, 
            hasHat: false
        }
    },
    mark: [89, 92, { exam: [1, 2, 3] }]
};

let b = Object();

deepCopy(b, a);

a.name = 'Alien';
a.func = () => { console.log('Wassup!'); };
a.prop.age = 1024;
a.prop.info.hasShirt = false;
a.mark[0] = 87;
a.mark[1] = 91;
a.mark[2].exam = [4, 5, 6];

console.log(a); // updated props
console.log(b);

Z hlediska efektivnosti se domnívám, že se jedná o nejjednodušší a nejefektivnější řešení problému, který jsem měl. Ocenil bych jakékoliv komentáře k tomuto algoritmu, které by ho mohly zefektivnit.

1
Kamyar

Když je váš objekt vnořený a obsahuje datový objekt, jiný strukturovaný objekt nebo nějaký objekt vlastnosti, atd. Pak pomocí JSON.parse(JSON.stringify(object)) nebo Object.assign({}, obj) nebo $.extend(true, {}, obj) nebude fungovat. V takovém případě použijte ubytování. Je to jednoduché a snadné ..

var obj = {a: 25, b: {a: 1, b: 2}, c: new Date(), d: anotherNestedObject };
var A = _.cloneDeep(obj);

Nyní bude A vaše nové klonované obj bez jakýchkoli odkazů .. 

1
Prasanth Jaya

Snad to pomůže.

function deepClone(obj) {
    /*
     * Duplicates an object 
     */

    var ret = null;
    if (obj !== Object(obj)) { // primitive types
        return obj;
    }
    if (obj instanceof String || obj instanceof Number || obj instanceof Boolean) { // string objecs
        ret = obj; // for ex: obj = new String("Spidergap")
    } else if (obj instanceof Date) { // date
        ret = new obj.constructor();
    } else
        ret = Object.create(obj.constructor.prototype);

    var prop = null;
    var allProps = Object.getOwnPropertyNames(obj); //gets non enumerables also


    var props = {};
    for (var i in allProps) {
        prop = allProps[i];
        props[prop] = false;
    }

    for (i in obj) {
        props[i] = i;
    }

    //now props contain both enums and non enums 
    var propDescriptor = null;
    var newPropVal = null; // value of the property in new object
    for (i in props) {
        prop = obj[i];
        propDescriptor = Object.getOwnPropertyDescriptor(obj, i);

        if (Array.isArray(prop)) { //not backward compatible
            prop = prop.slice(); // to copy the array
        } else
        if (prop instanceof Date == true) {
            prop = new prop.constructor();
        } else
        if (prop instanceof Object == true) {
            if (prop instanceof Function == true) { // function
                if (!Function.prototype.clone) {
                    Function.prototype.clone = function() {
                        var that = this;
                        var temp = function tmp() {
                            return that.apply(this, arguments);
                        };
                        for (var ky in this) {
                            temp[ky] = this[ky];
                        }
                        return temp;
                    }
                }
                prop = prop.clone();

            } else // normal object 
            {
                prop = deepClone(prop);
            }

        }

        newPropVal = {
            value: prop
        };
        if (propDescriptor) {
            /*
             * If property descriptors are there, they must be copied
             */
            newPropVal.enumerable = propDescriptor.enumerable;
            newPropVal.writable = propDescriptor.writable;

        }
        if (!ret.hasOwnProperty(i)) // when String or other predefined objects
            Object.defineProperty(ret, i, newPropVal); // non enumerable

    }
    return ret;
}

https://github.com/jinujd/Javascript-Deep-Clone

1

Pokud používáte es6, můžete to jednoduše provést pomocí operátoru spread.

let a = {id:1, name:'sample_name'}
let b = {...a};
1
Amaldev ps

Operátor Spread můžete použít klonování objektu v JavaScriptu.

// šíření operátora, který dělá práci concat

let arr = [1,2,3];  let arr2 = [4,5];  arr = [...arr,...arr2]; console.log(arr); 
0
Raghul Shree

Co takhle sloučit klíče objektu s jeho hodnoty?

function deepClone(o) {
    var keys = Object.keys(o);
    var values = Object.values(o);

    var clone = {};

    keys.forEach(function(key, i) {
        clone[key] = typeof values[i] == 'object' ? Object.create(values[i]) : values[i];
    });

    return clone;
}

Poznámka: Tato metoda nemusí nutně vytvářet mělké kopie, ale kopíruje pouze s hloubkou jednoho vnitřního objektu, což znamená, že když dostanete něco jako {a: {b: {c: null}}}, bude klonovat pouze objekty, které jsou přímo uvnitř nich, takže deepClone(a.b).c je technicky odkaz na a.b.c, zatímco deepClone(a).b je klon, ne odkaz.

0

S návrhem nové metody Object.fromEntries () , která je podporována na novějších verzích některých prohlížečů ( reference ). Chci přispět dalším rekurzivním přístupem:

const obj = {
  key1: {key11: "key11", key12: "key12", key13: {key131: 22}},
  key2: {key21: "key21", key22: "key22"},
  key3: "key3",
  key4: [1,2,3, {key: "value"}]
}

const cloneObj = (obj) =>
{
    if (Object(obj) !== obj)
       return obj;
    else if (Array.isArray(obj))
       return obj.map(cloneObj);

    return Object.fromEntries(Object.entries(obj).map(
        ([k,v]) => ([k, cloneObj(v)])
    ));
}

// Clone the original object.
let newObj = cloneObj(obj);

// Make changes on the original object.
obj.key1.key11 = "TEST";
obj.key3 = "TEST";
obj.key1.key13.key131 = "TEST";
obj.key4[1] = "TEST";
obj.key4[3].key = "TEST";

// Display both objects on the console.
console.log("Original object: ", obj);
console.log("Cloned object: ", newObj);
.as-console {background-color:black !important; color:Lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
0
Shidersz
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.");
}

místo JSON.parse(JSON.stringify(obj)) použijte následující metodu, protože je pomalejší než následující metoda 

Jak správně klonuji objekt JavaScriptu?

0
shakthi nagaraj