Jaki jest najbardziej efektywny sposób na głębokie sklonowanie obiektu w JavaScript?

Jaki jest najbardziej efektywny sposób sklonowania obiektu JavaScript? Widziałem obj = eval(uneval(o)); używane, ale to niestandardowe i obsługiwane tylko przez Firefoksa .

robiłem takie rzeczy jak obj = JSON.parse(JSON.stringify(o));, ale kwestionuj skuteczność.

widziałem również rekurencyjne funkcje kopiowania z różnymi wadami.
Dziwię się, że nie ma kanonicznego rozwiązania.

Author: jschrab, 0000-00-00

30 answers

Uwaga: jest to odpowiedź na inną odpowiedź, a nie właściwa odpowiedź na to pytanie. Jeśli chcesz mieć szybkie klonowanie obiektów, postępuj zgodnie z radami Corbana w odpowiedzi na to pytanie.


Chcę zauważyć, że .clone() metoda w jQuery klonuje tylko elementy DOM. Aby sklonować Obiekty JavaScript, wykonałbyś:

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

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

Więcej informacji można znaleźć w jQuery documentation .

Ja również warto zauważyć, że deep copy jest znacznie mądrzejszy od tego, co pokazano powyżej – jest w stanie uniknąć wielu pułapek(na przykład próba głębokiego rozszerzenia elementu DOM). Jest często używany w jQuery core i w wtyczkach z wielkim skutkiem.

 4123
Author: Nidhin David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-24 15:17:12

Sprawdź ten benchmark: http://jsben.ch/#/bWfk9

W moich poprzednich testach, gdzie prędkość była głównym problemem znalazłem

JSON.parse(JSON.stringify(obj))

Aby być najszybszym sposobem na głębokie sklonowanie obiektu (bije się jQuery.extend with deep flag set true by 10-20%).

JQuery.extend jest dość szybki, gdy flaga deep jest ustawiona na false (shallow clone). Jest to dobra opcja, ponieważ zawiera dodatkową logikę walidacji typu i nie kopiuje niezdefiniowanych właściwości, itd. ale to również trochę cię spowolni.

Jeśli znasz strukturę obiektów, które próbujesz sklonować lub możesz uniknąć głębokich zagnieżdżonych tablic, możesz napisać prostą pętlę for (var i in obj), aby sklonować obiekt podczas sprawdzania hasOwnProperty i będzie to znacznie szybsze niż jQuery.

Wreszcie, jeśli próbujesz sklonować znaną strukturę obiektu w gorącej pętli, możesz uzyskać znacznie większą wydajność, po prostu wprowadzając procedurę klonowania i ręcznie konstruując obiekt.

Silniki śledzenia JavaScript są do bani w optymalizacji for..in pętli i sprawdzanie hasOwnProperty będzie również spowolnić. Ręczny klon, gdy prędkość jest absolutną koniecznością.

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Uwaga przy użyciu metody JSON.parse(JSON.stringify(obj)) na obiektach Date - JSON.stringify(new Date()) zwraca reprezentację łańcuchową daty w formacie ISO, która JSON.parse() nie konwertuje z powrotem do Date obiektu. Zobacz tę odpowiedź po więcej szczegółów .

Dodatkowo należy pamiętać, że w Chrome 65 przynajmniej natywny klonowanie nie jest dobrym rozwiązaniem. Według ten JSPerf, wykonywanie natywnego klonowania poprzez utworzenie nowej funkcji jest prawie 800x wolniejsze niż używanie JSON.stringify, który jest niesamowicie szybki na całej planszy.

 1951
Author: Corban Brook,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-07 01:18:24

Zakładając, że w obiekcie masz tylko zmienne, a nie Funkcje, możesz po prostu użyć:

var newObject = JSON.parse(JSON.stringify(oldObject));
 412
Author: Sultan Shakir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-23 23:47:23

Jeśli nie było żadnego wbudowanego, możesz spróbować:

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;
}
 285
Author: ConroyP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-27 08:59:03

Klonowanie Strukturalne

HTML5 definiuje wewnętrzny "ustrukturyzowany" algorytm klonowania które mogą tworzyć głębokie klony obiektów. Jest on nadal ograniczony do pewnych wbudowanych typów, ale oprócz kilku typów obsługiwanych przez JSON obsługuje również daty, wyrażenia regularne, Mapy, Zestawy, obiekty BLOB, listy plików, ImageDatas, rzadkie Tablice, typowane Tablice, i prawdopodobnie więcej w przyszłości. Zachowuje również odniesienia w sklonowanych danych, co pozwala na obsługę cyklicznych i struktury rekurencyjne, które powodowałyby błędy w JSON.

Bezpośrednie wsparcie w przeglądarkach: już wkrótce?

Przeglądarki nie zapewniają obecnie bezpośredniego interfejsu dla strukturalnego algorytmu klonowania, ale globalna funkcja structuredClone() jest aktywnie omawiana w whatwg/html#793 na GitHub i może pojawić się wkrótce! Jak obecnie proponuje, używanie go do większości celów będzie tak proste, jak: {]}

const clone = structuredClone(original);

Dopóki to nie zostanie wysłane, strukturalne implementacje klonów przeglądarek są tylko narażone pośrednio.

Asynchroniczne Obejście: Użyteczne.

Aby utworzyć klon strukturalny z istniejącymi interfejsami API, należy przesyłać dane przez jeden port MessageChannels . Drugi port będzie emitował Zdarzenie message ze strukturalnym klonem dołączonego .data. Niestety, słuchanie tych zdarzeń jest koniecznie asynchroniczne, a synchroniczne alternatywy są mniej praktyczne.
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);

Przykład Użycie:

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

Synchroniczne Obejścia: Okropne!

Nie ma dobrych opcji tworzenia synchronicznie uporządkowanych klonów. Oto kilka niepraktycznych hacków.

history.pushState() i history.replaceState() tworzą uporządkowany klon pierwszego argumentu i przypisują tę wartość history.state. Możesz użyć tego do utworzenia strukturalnego klonu dowolnego obiektu, takiego jak ten:

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

Przykładowe Użycie:

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

Choć synchroniczne, to może być bardzo wolno. Ponosi wszystkie koszty związane z manipulowaniem historią przeglądarki. Wielokrotne wywołanie tej metody może spowodować, że Chrome tymczasowo przestanie odpowiadać.

The Notification konstruktor tworzy strukturalny klon powiązanych z nim danych. Próbuje również wyświetlić Użytkownikowi powiadomienie przeglądarki, ale to po cichu się nie powiedzie, chyba że poprosiłeś o pozwolenie powiadomienia. Jeśli masz pozwolenie na inne cele, natychmiast zamkniemy powiadomienie, które stworzyliśmy.

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

Przykładowe Użycie:

'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();
 284
Author: Jeremy Banks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-28 21:07:33

Efektywny sposób klonowania (nie deep-clone) obiektu w jednej linii kodu

An Object.assign metoda jest częścią standardu ECMAScript 2015 (ES6) i robi dokładnie to, czego potrzebujesz.

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

Obiekt.metoda assign () służy do kopiowania wartości wszystkich wyliczalnych właściwości własnych z jednego lub więcej obiektów źródłowych do obiektu docelowego.

Czytaj więcej...

The polyfill do obsługi starszych przeglądarek:

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;
    }
  });
}
 140
Author: Eugene Tiurin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-14 08:46:58

Kod:

// 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);
 91
Author: Kamarey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-26 14:20:09

To jest to, czego używam:

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;
}
 81
Author: Alan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-16 16:37:34

Głębokie kopiowanie według wydajności: Ranking od najlepszego do najgorszego

  • Resignment " = "(tablice łańcuchowe, tablice liczbowe - tylko)
  • Slice (tablice łańcuchowe, tablice liczbowe - tylko)
  • Concatenation (tablice łańcuchowe, tablice liczbowe - tylko)
  • Custom function: for-loop or recursive copy
  • jQuery to $.extend
  • JSON.parse (tablice łańcuchowe, tablice liczbowe, tablice obiektów - tylko)
  • podkreślenie.js ' s _.clone (tablice łańcuchowe, liczba arrays-only)
  • Lo-Dash ' s _.cloneDeep

Głębokie kopiowanie tablicy ciągów lub liczb (jeden poziom - bez wskaźników referencyjnych):

Gdy tablica zawiera liczby i łańcuchy-funkcje takie jak .slice (),concat (),splice (), operator przypisania " = " i podkreślenie.funkcja klonowania js; zrobi głęboką kopię elementów tablicy.

Gdzie zmiana ma najszybszą wydajność:

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

I .slice () ma lepszą wydajność niż .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

Głębokie kopiowanie tablicy obiektów (dwa lub więcej poziomów-wskaźniki referencyjne):

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

Napisz funkcję niestandardową (ma szybszą wydajność niż $.extend() lub 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);

Użyj funkcji narzędzi innych firm:

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

Gdzie jQuery jest $.extend ma lepiej wydajność:

 67
Author: tfmontague,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-20 15:11:04
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});
 57
Author: Zibri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-21 15:01:15

Wiem, że to stary post, ale pomyślałem, że może to pomóc następnej osobie, która się potyka.

Dopóki nie przypisujesz obiektu do niczego, nie przechowuje on odniesienia w pamięci. Aby utworzyć obiekt, który chcesz udostępnić między innymi obiektami, musisz utworzyć fabrykę w ten sposób:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
 50
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-17 01:18:29

Istnieje biblioteka (zwana "klonem"), która robi to całkiem dobrze. Zapewnia najbardziej kompletne rekurencyjne klonowanie / kopiowanie dowolnych obiektów, jakie znam. Obsługuje również odniesienia okrągłe, które nie są jeszcze objęte innymi odpowiedziami.

Możesz znaleźć go również na npm . Może być używany zarówno dla przeglądarki, jak i węzła.js.

Oto przykład jak z niego korzystać:

Zainstaluj go za pomocą

npm install clone

Lub spakować go z Ender.

ender build clone [...]

Możesz również pobrać kod źródłowy ręcznie.

Wtedy możesz użyć go w swoim kodzie źródłowym.

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' } }

(Zastrzeżenie: jestem autorem biblioteki.)

 48
Author: pvorb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-27 08:04:54

Cloning obiekt był zawsze problemem w JS, ale chodziło o to przed ES6, poniżej wymieniam różne sposoby kopiowania obiektu w JavaScript, wyobraź sobie, że masz obiekt poniżej i chciałbyś mieć głęboką kopię tego: {]}

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

Istnieje kilka sposobów skopiowania tego obiektu, bez zmiany pochodzenia:

1) ES5+, używając prostej funkcji do wykonania kopii za ciebie:

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+, używając JSON.parse i 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
Mam nadzieję, że to pomoże...
 47
Author: Alireza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-24 17:35:02

Jeśli go używasz, podkreśl .biblioteka js posiada metodę clone.

var newObject = _.clone(oldObject);
 46
Author: itsadok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-22 17:26:12

Oto wersja powyższej odpowiedzi Conroypa, która działa nawet jeśli konstruktor ma wymagane 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;
}

Ta funkcja jest również dostępna w mojej bibliotece simpleoo .

Edit:

Oto bardziej rozbudowana wersja (dzięki Justinowi Mccandlessowi obsługuje teraz również cykliczne odniesienia):

/**
 * 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();
    };
}
 34
Author: Matt Browne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-12 18:02:45

Poniższe tworzy dwie instancje tego samego obiektu. Znalazłem go i używam go obecnie. Jest prosty i łatwy w użyciu.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));
 30
Author: nathan rogers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-01 16:06:53
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
 22
Author: Mark Cidade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 16:45:39

Crockford sugeruje (a ja wolę) użycie tej funkcji:

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

var newObject = object(oldObject);

Jest zwięzły, działa zgodnie z oczekiwaniami i nie potrzebujesz biblioteki.


EDIT:

To jest polyfill dla Object.create, więc możesz również użyć tego.

var newObject = Object.create(oldObject);

Uwaga: Jeśli używasz niektórych z nich, możesz mieć problemy z niektórymi iteracjami używającymi hasOwnProperty. Ponieważ, create tworzy nowy pusty obiekt, który dziedziczy oldObject. Ale nadal jest przydatny i praktyczny do klonowania obiektów.

Dla exemple if oldObject.a = 5;

newObject.a; // is 5

Ale:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
 20
Author: protonfish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-12 14:59:41

Lodash ma ładny _.metoda cloneDeep (value):

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

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
 20
Author: opensas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-31 12:59:40

Shallow copy one-liner ( ECMAScript 5th edition):

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

I płytka Kopia one-liner ( ECMAScript 6th edition, 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
 18
Author: Maël Nison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-22 17:27:51

Tylko dlatego, że nie widziałem AngularJS wspomniał i pomyślałem, że ludzie mogą chcieć wiedzieć...

angular.copy zapewnia również metodę głębokiego kopiowania obiektów i tablic.

 15
Author: Dan Atkinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-15 18:38:31

Wydaje się, że nie istnieje jeszcze idealny Operator głębokiego klonu dla obiektów podobnych do tablicy. Jak ilustruje poniższy kod, jQuery Cloner Johna Resiga zamienia tablice z nie-numerycznymi właściwościami w obiekty, które nie są tablicami, a JSON Cloner Regdwighta usuwa nie-numeryczne właściwości. Poniższe testy ilustrują te punkty w wielu przeglądarkach:

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)
 14
Author: Page Notes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-13 19:00:03

Mam dwie dobre odpowiedzi w zależności od tego, czy Twoim celem jest sklonowanie "zwykłego starego obiektu JavaScript", czy nie.

Załóżmy również, że twoim zamiarem jest stworzenie kompletnego klonu bez odniesień do prototypu z powrotem do obiektu źródłowego. Jeśli nie jesteś zainteresowany kompletnym klonem, możesz użyć wielu obiektów.procedury clone () podane w niektórych innych odpowiedziach (wzór Crockforda).

Dla zwykłych starych obiektów JavaScript, wypróbowany i prawdziwy dobry sposób na klonowanie obiekt w nowoczesnym środowisku uruchomieniowym jest po prostu:

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

Zauważ, że obiekt źródłowy musi być czystym obiektem JSON. Oznacza to, że wszystkie jego zagnieżdżone właściwości muszą być skalarami (jak boolean, string, array, object, itd.). Wszelkie funkcje lub obiekty specjalne, takie jak Wyrażenie regularne lub data, nie będą klonowane.

Czy jest skuteczny? Jasne, że tak. Próbowaliśmy WSZYSTKICH rodzajów metod klonowania i to działa najlepiej. Jestem pewien, że jakiś ninja mógłby wymyślić szybszą metodę. Ale podejrzewam, że mówimy o marginalnym zyski.

To podejście jest proste i łatwe do wdrożenia. Zawiń go w funkcję wygody i jeśli naprawdę potrzebujesz wycisnąć jakiś zysk, idź na później.

Teraz, dla nie-zwykłych obiektów JavaScript, nie ma naprawdę prostej odpowiedzi. W rzeczywistości nie może być ze względu na dynamiczny charakter funkcji JavaScript i wewnętrzny stan obiektu. Głębokie klonowanie struktury JSON z funkcjami wewnątrz wymaga odtworzenia tych funkcji i ich wewnętrznego kontekstu. I JavaScript po prostu nie ma standaryzowanego sposobu na to.

Poprawnym sposobem jest użycie wygodnej metody, którą deklarujesz i używasz ponownie w swoim kodzie. Metoda wygody może być obdarzona pewnym zrozumieniem własnych obiektów, dzięki czemu można upewnić się, że poprawnie odtworzysz wykres w nowym obiekcie.

Jesteśmy Pisani sami, ale najlepsze ogólne podejście, jakie widziałem, obejmuje tutaj:

Http://davidwalsh.name/javascript-clone

To jest dobry pomysł. Autor (David Walsh) skomentował klonowanie funkcji uogólnionych. Jest to coś, co możesz wybrać, w zależności od przypadku użycia.

Główną ideą jest to, że musisz specjalnie obsługiwać instancję swoich funkcji (lub klas prototypowych, że tak powiem)na podstawie każdego typu. Oto kilka przykładów wyrażenia regularnego i daty.

Nie tylko ten kod krótki, ale też bardzo czytelny. Jest dość łatwy do rozszerzenia.

Czy to jest skuteczne? Jasne, że tak. Biorąc pod uwagę, że celem jest stworzenie prawdziwego klona głębokiej kopii, będziesz musiał przejść przez członków wykresu obiektu źródłowego. Dzięki temu podejściu możesz dokładnie dostosować, które elementy podrzędne mają być traktowane i jak ręcznie obsługiwać niestandardowe typy. No i proszę. Dwa podejścia. Moim zdaniem oba są skuteczne.
 13
Author: Michael Uzquiano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-22 17:33:57

To nie jest najskuteczniejsze rozwiązanie, ale robi to, czego potrzebuję. Proste przypadki testowe poniżej...

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 tablicy cyklicznej...

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

Test funkcji...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
 11
Author: neatonk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-03 02:08:03

AngularJS

Jeśli używasz angular, możesz też to zrobić.]}
var newObject = angular.copy(oldObject);
 9
Author: azerafati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-14 13:26:15

Nie zgadzam się z odpowiedzią z największymi głosami Tutaj . Rekurencyjny Klon głęboki jest znacznie szybszyniż JSON.parse (JSON.stringify (obj))

A oto funkcja dla szybkiego odniesienia:

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
}
 9
Author: prograhammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-18 06:34:37
// 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;
};
 9
Author: Dima,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-17 16:27:11

Oto kompleksowa metoda clone (), która może sklonować dowolny obiekt JavaScript. Obsługuje prawie wszystkie przypadki:

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;
};
 7
Author: user1547016,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-22 17:31:06

Dla osób, które chcą użyć wersji JSON.parse(JSON.stringify(obj)), ale bez utraty obiektów Date, możesz użyć drugiego argumentu metody parse do konwersji łańcuchów z powrotem 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;
  });
}
 7
Author: Buzinas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-22 17:47:24

Zazwyczaj używam var newObj = JSON.parse( JSON.stringify(oldObje) ); ale tutaj jest bardziej odpowiedni sposób:

var o = {};

var oo = Object.create(o);

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

Oglądaj starsze przeglądarki!

 6
Author: Cody,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-12 18:44:01