Jak poprawnie sklonować obiekt JavaScript?

Mam obiekt, x. Chciałbym skopiować go jako obiekt y, tak aby zmiany na y nie zmieniały x. Zdałem sobie sprawę, że kopiowanie obiektów pochodzących z wbudowanych obiektów JavaScript spowoduje dodatkowe, niechciane właściwości. To nie jest problem, ponieważ kopiuję jeden z moich własnych, dosłownie skonstruowanych obiektów.

Jak poprawnie sklonować obiekt JavaScript?

Author: mindeavor, 2009-04-08

30 answers

Aby to zrobić dla dowolnego obiektu w JavaScript nie będzie proste lub proste. Napotkasz problem błędnego pobierania atrybutów z prototypu obiektu, które powinny zostać pozostawione w prototypie, a nie skopiowane do nowej instancji. Jeśli na przykład dodajesz metodę clone do Object.prototype, Jak przedstawiają niektóre odpowiedzi, będziesz musiał jawnie pominąć ten atrybut. Ale co jeśli są inne dodatkowe metody dodane do Object.prototype, lub inne prototypy pośrednie, których nie o czym? W takim przypadku skopiujesz atrybuty, których nie powinieneś, więc musisz wykryć nieprzewidziane, nielokalne atrybuty za pomocąhasOwnProperty metoda.

Oprócz niewyliczalnych atrybutów napotkasz trudniejszy problem podczas kopiowania obiektów, które mają ukryte właściwości. Na przykład prototype jest ukrytą właściwością funkcji. Ponadto, do prototypu obiektu odnosi się atrybut __proto__, który jest również ukryty i nie będzie kopiowany przez iterację pętli for / in nad atrybutami obiektu źródłowego. Myślę, że __proto__ może być specyficzne dla interpretera JavaScript Firefoksa i może to być coś innego w innych przeglądarkach, ale masz obraz. Nie wszystko da się wyliczyć. Możesz skopiować Ukryty atrybut, jeśli znasz jego nazwę, ale nie wiem, jak go odkryć automatycznie.

Kolejnym problemem w poszukiwaniu eleganckiego rozwiązania jest problem prawidłowego ustawienia dziedziczenia prototypu. Jeśli prototyp Twojego obiektu źródłowego jest Object, wtedy po prostu utworzenie nowego obiektu ogólnego za pomocą {} zadziała, ale jeśli prototyp źródłowy jest potomkiem Object, to będzie brakować dodatkowych elementów z tego prototypu, które pominąłeś za pomocą filtra hasOwnProperty, lub które były w prototypie, ale nie były wyliczalne w pierwszej kolejności. Jednym z rozwiązań może być wywołanie właściwości constructor obiektu źródłowego, aby uzyskać początkową kopię obiektu, a następnie skopiować atrybuty, ale nadal nie otrzymasz atrybuty niewyliczalne. Na przykład, a Date obiekt przechowuje swoje dane jako ukryty członek:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

Ciąg daty dla d1 będzie 5 sekund za ciągiem d2. Sposobem na uczynienie jednej Date tym samym co drugiej jest wywołanie metody setTime, ale jest to specyficzne dla klasy Date. Nie sądzę, że istnieje kuloodporne ogólne rozwiązanie tego problemu, choć byłbym szczęśliwy, że się mylę!

Kiedy musiałem wdrożyć ogólne Głębokie kopiowanie skończyło się kompromitując zakładając, że wystarczy skopiować zwykły Object, Array, Date, String, Number, lub Boolean. Ostatnie 3 typy są niezmienne, więc mógłbym wykonać płytką kopię i nie martwić się o to, że się zmieni. Ponadto założyłem, że dowolne elementy zawarte w Object lub Array będą również jednym z 6 prostych typów na tej liście. Można to osiągnąć za pomocą następującego kodu:

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

Powyższa funkcja będzie działać odpowiednio dla 6 prostych typów, o których wspomniałem, o ile dane w obiektach i tablicach tworzą strukturę drzewiastą. Oznacza to, że w obiekcie nie ma więcej niż jednego odniesienia do tych samych danych. Na przykład:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

Nie będzie w stanie obsłużyć żadnego obiektu JavaScript, ale może być wystarczający do wielu celów, o ile nie założysz, że będzie działał tylko na cokolwiek rzucisz na niego.

 1345
Author: A. Levy,
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-08-14 15:12:54

Z jQuery możesz skopiować z extend:

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

Kolejne zmiany w copiedObject nie będą miały wpływu na originalObject i vice versa.

Lub zrobić głęboką kopię :

var copiedObject = jQuery.extend(true, {}, originalObject)
 728
Author: Pascal,
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-08-18 13:28:04

Jeśli nie używasz funkcji w swoim obiekcie, bardzo prosta jedna linijka może być następująca:

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

To działa dla wszystkich rodzajów obiektów zawierających obiekty, tablice, łańcuchy znaków, wartości logiczne i liczby.

Zobacz także Ten artykuł o strukturalnym algorytmie klonowania przeglądarek , który jest używany podczas wysyłania wiadomości do i od pracownika. Zawiera również funkcję głębokiego klonowania.

 720
Author: heinob,
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-01-26 14:44:29

W ECMAScript 6 znajduje się Obiekt.Przypisz metodę , która kopiuje wartości wszystkich możliwych do wyliczenia własnych właściwości z jednego obiektu do drugiego. Na przykład:

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

Ale należy pamiętać, że zagnieżdżone obiekty są nadal kopiowane jako odniesienia.

 531
Author: Vitalii Fedorenko,
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-21 11:14:00

Per MDN :

  • Jeśli chcesz skopiować płytkę, użyj Object.assign({}, a)
  • do kopiowania" głębokiego " użyj JSON.parse(JSON.stringify(a))

Nie ma potrzeby korzystania z zewnętrznych bibliotek, ale najpierw należy sprawdzić zgodność przeglądarki .

 125
Author: Tareq,
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-08 07:42:24

Istnieje wiele odpowiedzi, ale żadna nie wspomina o obiekcie.Utwórz z ECMAScript 5, który co prawda nie daje dokładnej kopii, ale ustawia źródło jako prototyp nowego obiektu.

Nie jest to więc dokładna odpowiedź na pytanie, ale rozwiązanie jednoliniowe, a przez to eleganckie. I działa najlepiej w 2 Przypadkach:
  1. gdzie takie dziedziczenie jest przydatne (duh!)
  2. gdzie obiekt źródłowy nie zostanie zmodyfikowany, dzięki czemu relacja między 2 obiekty nie będące wydaniem.

Przykład:

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

Dlaczego uważam to rozwiązanie za lepsze? Jest natywny, więc nie ma pętli, nie ma rekurencji. Jednak starsze przeglądarki będą potrzebować polyfill.

 119
Author: itpastorn,
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
2012-03-19 15:17:52

Elegancki sposób klonowania obiektu Javascript w jednej linii kodu

Metoda Object.assign 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;
    }
  });
}
 106
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
2015-12-15 16:42:26

Jeśli nie masz nic przeciwko płytkiej kopii, podkreśl.biblioteka js posiada metodę clone .

y = _.clone(x);

Lub możesz go rozszerzyć jak

copiedObject = _.extend({},originalObject);
 71
Author: dule,
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-01-19 18:47:56

Istnieje kilka problemów z większością rozwiązań w Internecie. Więc postanowiłem zrobić kontynuację, która obejmuje, dlaczego przyjęta odpowiedź nie powinna być akceptowana.

Sytuacja wyjściowa

Chcę głęboko skopiować Javascript Object ze wszystkimi Jego dziećmi i ich dziećmi i tak dalej. Ale ponieważ nie jestem jakimś normalnym programistą, mój Object ma normalny properties, circular structures a nawet nested objects.

Więc stwórzmy circular structure i nested object najpierw.

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

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

Połączmy wszystko w Object o nazwie a.

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

Następnie chcemy skopiować a do zmiennej o nazwie b i zmutować ją.

var b = a;

b.x = 'b';
b.nested.y = 'b';
Wiesz, co tu się stało, bo jeśli nie, to nawet nie wylądowałbyś na tym wielkim pytaniu.
console.log(a, b);

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

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}
Znajdźmy rozwiązanie.

JSON

Pierwszą próbą, jaką próbowałem było użycie JSON.

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

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

Nie trać na to zbyt wiele czasu, dostaniesz TypeError: Converting circular structure to JSON.

Kopia rekurencyjna (akceptowana "odpowiedź")

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

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

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

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

    throw new Error("Unable to copy obj! Its type isn't supported.");
}
Wygląda dobrze, heh? Jest to rekurencyjna Kopia obiektu i obsługuje również inne typy, takie jak Date, ale nie było to wymagane.
var b = cloneSO(a);

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

Rekurencja i circular structures nie działają dobrze razem... RangeError: Maximum call stack size exceeded

Rozwiązanie natywne

Po kłótni z moim współpracownikiem, mój szef zapytał nas, co się stało, i znalazł proste rozwiązanie po jakimś googlowaniu. Nazywa się Object.create.

var b = Object.create(a);

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

To rozwiązanie zostało dodane do Javascript jakiś czas temu, a nawet obsługuje circular structure.

console.log(a, b);

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

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

... i widzisz, to nie działa z zagnieżdżoną strukturą wewnątrz.

Polyfill dla roztworu natywnego

W starszej przeglądarce jest polyfill dla Object.create, tak jak w IE 8. To coś takiego jak polecane przez Mozillę i oczywiście nie jest idealne i skutkuje tym samym problemem co rozwiązanie natywne .

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

var b = clonePF(a);

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

Umieściłem F poza zasięgiem, żebyśmy mogli przyjrzeć się temu, co mówi nam {34]}.

console.log(a, b);

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

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

console.log(typeof a, typeof b);

a --> object
b --> object

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

a --> true
b --> true

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

a --> false
b --> true

Ten sam problem co natywne rozwiązanie , ale trochę gorszy wynik.

Lepsze (ale nie idealne) rozwiązanie

Podczas kopania, znalazłem podobne pytanie ( w Javascript, podczas wykonywania głębokiej kopii, jak uniknąć cyklu, ponieważ właściwość jest "to"?) do tego, ale o wiele lepiej rozwiązanie.

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

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

var b = cloneDR(a);

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

I spójrzmy na wyjście...

console.log(a, b);

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

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

console.log(typeof a, typeof b);

a --> object
b --> object

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

a --> true
b --> true

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

a --> false
b --> false

Wymagania są dopasowane, ale nadal istnieją pewne mniejsze problemy, w tym zmiana instance z nested i circ na Object.

Struktura drzew, które mają wspólny liść, nie zostanie skopiowana, staną się dwoma niezależnymi liśćmi:]}
        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

Podsumowanie

Nie jest to jednak zbyt dobre rozwiązanie, ponieważ jest to realne rozwiązanie, które może być używane przez rekurencję i pamięć podręczną.]} deep-Kopia obiektu. Obsługuje proste properties, circular structures i nested object, ale spowoduje to bałagan w ich instancji podczas klonowania.

Http://jsfiddle.net/einfallstoll/N4mr2/

 66
Author: Fabio Poloni,
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-11-25 10:44:05

Szczególnie nieeleganckim rozwiązaniem jest użycie kodowania JSON do tworzenia głębokich kopii obiektów, które nie mają metod członkowskich. Metodologia polega na zakodowaniu obiektu docelowego JSON, a następnie dekodowaniu go, otrzymujesz kopię, której szukasz. Możesz dekodować tyle razy, ile chcesz, aby wykonać tyle kopii, ile potrzebujesz.

Oczywiście, funkcje nie należą do JSON, więc działa to tylko dla obiektów bez metod członkowskich.

Ta metodologia była idealna dla mojego przypadku użycia, ponieważ jestem przechowywanie obiektów blob JSON w magazynie klucz-wartość, a kiedy są eksponowane jako obiekty w JavaScript API, każdy obiekt faktycznie zawiera kopię pierwotnego stanu obiektu, więc możemy obliczyć delta po tym, jak wywołujący zmutował eksponowany obiekt.

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

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

object2.key = "a change";
console.log(object1);// returns value
 37
Author: Tim,
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-28 17:24:12

Możesz po prostu użyć właściwości spread, Aby skopiować obiekt bez referencji. Ale uważaj (patrz komentarze), 'kopia' jest tylko na najniższym poziomie obiektu / tablicy. Zagnieżdżone właściwości są nadal referencjami!


Kompletny klon:

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

// => mutate without references:

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

Klon z odniesieniami na drugim poziomie:

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

// => nested object is still a references:

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

JavaScript faktycznie nie obsługuje głębokich klonów natywnie. Użyj funkcji użytkowej. Na przykład Ramda:

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

 25
Author: musemind,
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-04-05 12:30:15

Dla tych, którzy używają AngularJS, istnieje również bezpośrednia metoda klonowania lub rozszerzania obiektów w tej bibliotece.

var destination = angular.copy(source);

Lub

angular.copy(source, destination);

Więcej w angular.Kopia dokumentacja...

 23
Author: Lukas Jelinek,
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-09-03 19:08:28

Ok, wyobraź sobie, że masz ten obiekt poniżej i chcesz go sklonować:

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

Lub

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

Odpowiedź jest głównie Depened, na którym ECMAscript używasz, w ES6+, możesz po prostu użyć Object.assign aby wykonać klon:

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

Lub używając operatora spreadu w ten sposób:

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

Ale jeśli używasz ES5, możesz użyć kilku metod, ale JSON.stringify, po prostu upewnij się, że nie używasz dużej ilości danych do skopiowania, ale może to być jedna linia przydatna w wielu przypadki, coś takiego:

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over
 22
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
2017-07-06 22:48:04

Odpowiedź A. Levy ' ego jest prawie kompletna, oto mój mały wkład: istnieje sposób, jak obsługiwać odwołania rekurencyjne, Zobacz ten wiersz

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

Jeśli obiekt jest elementem XML DOM, musimy użyć cloneNode zamiast

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

Zainspirowany wyczerpującym badaniem A. Levy ' ego i podejściem do prototypowania Calvina, oferuję takie rozwiązanie:

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

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

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

Zobacz także notatkę Andy 'ego Burke' a w odpowiedziach.

 20
Author: Jan Turoň,
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
2012-12-02 20:49:01

Z tego artykułu: Jak kopiować tablice i obiekty w Javascript by Brian Huisman:

Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (var i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
      newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
  } return newObj;
};
 19
Author: Calvin,
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
2012-09-22 22:06:59

W ES-6 można po prostu użyć obiektu.assign(...). Ex:

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

Dobre odniesienie jest tutaj: https://googlechrome.github.io/samples/object-assign-es6/

 18
Author: João Oliveira,
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-04-13 20:17:11

Oto funkcja, której możesz użyć.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}
 17
Author: picardo,
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
2012-02-23 20:04:41

Możesz sklonować obiekt i usunąć wszelkie odniesienia z poprzedniego za pomocą jednej linii kodu. Po prostu zrób:

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

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

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

Dla przeglądarek / silników, które obecnie nie obsługują obiektu.Utwórz możesz użyć tego polyfill:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}
 14
Author: Rob Evans,
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
2012-09-16 05:27:01

Nowa odpowiedź na Stare pytanie! Jeśli masz przyjemność używać ECMAScript 2016 (ES6) z składnią rozproszoną , jest to proste.

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

Zapewnia to czystą metodę płytkiej kopii obiektu. Wykonanie głębokiej kopii, czyli makign nowej kopii każdej wartości w każdym rekurencyjnie zagnieżdżonym obiekcie, wymaga zastosowania cięższych rozwiązań powyżej.

JavaScript ciągle ewoluuje.

 11
Author: Charles Merriam,
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-16 11:34:33

Używając Lodash:

var y = _.clone(x, true);
 10
Author: VaZaA,
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
2012-12-13 00:05:07
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

Rozwiązanie ES6 jeśli chcesz (płytko) sklonować instancję klasy , a nie tylko obiekt Właściwości.

 10
Author: flori,
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-27 12:57:57

Zainteresowani klonowaniem prostych obiektów:

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

Source: Jak skopiować obiekt JavaScript do nowej zmiennej nie przez odniesienie?

 9
Author: Mohammed Akdim,
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-05-23 12:18:27

Powyższa odpowiedź Jana Turoňa jest bardzo bliska i może być najlepsza w przeglądarce ze względu na problemy ze zgodnością, ale może powodować dziwne problemy z wyliczaniem. Na przykład wykonanie:

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

Przypisze metodę clone() do i po iteracji elementów tablicy. Oto adaptacja, która unika wyliczania i działa z węzłem.js:

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

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

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

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

Pozwala to uniknąć wyliczenia metody clone (), ponieważ defineproperty () domyślnie wylicza fałsz.

 6
Author: Andy Burke,
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
2012-03-30 06:03:57

Jest to adaptacja kodu A. Levy ' ego do klonowania funkcji i wielokrotnych / cyklicznych odniesień - oznacza to, że jeśli dwie właściwości w drzewie, które jest sklonowane, są odniesieniami do tego samego obiektu, to klonowane drzewo obiektów będzie miało te właściwości wskazujące na jeden i ten sam klon odwołanego obiektu. Rozwiązuje to również przypadek cyklicznych zależności, które, jeśli pozostawione nieobsługiwane, prowadzą do nieskończonej pętli. Złożoność algorytmu wynosi O (n)

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

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

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

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

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

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

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

            clonedObjectsArray[objectId(obj)] = copy;

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

            return copy;
        }       


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



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

    return cloneObj;
}

Niektóre szybkie testy

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

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

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

objclone = clone(obj);

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

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

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
 6
Author: Radu Simionescu,
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
2012-07-16 09:23:13

Napisałam własną implementację. Nie wiem czy liczy się jako lepsze rozwiązanie:

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

Poniżej znajduje się implementacja:

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

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

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

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

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

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

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

    return result;
}
 6
Author: yazjisuhail,
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-29 16:32:06

Chciałem tylko dodać do wszystkich Object.create rozwiązań w tym poście, że to nie działa w pożądany sposób z nodejs.

W Firefoksie wynik

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

Jest

{test:"test"}.

W nodejs jest

{}
 5
Author: heinob,
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
2012-06-03 09:29:51
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;
};
 5
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
2012-07-31 20:53:16

Ponieważ mindeavor stwierdził, że obiekt, który ma być sklonowany, jest obiektem "skonstruowanym literalnie", rozwiązaniem może być po prostu wygenerowanie obiektu wiele razy, a nie klonowanie instancji obiektu:

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

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
 5
Author: Bert Regelink,
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-05-23 12:34:51

Consult http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data dla algorytmu W3C" Safe passing of structured data", przeznaczonego do implementacji przez przeglądarki do przekazywania danych np. webowym pracownikom. Ma jednak pewne ograniczenia, ponieważ nie obsługuje funkcji. Zobacz https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm aby uzyskać więcej informacji, w tym alternatywny algorytm w JS, który cię część drogi tam.

 4
Author: user663031,
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-04-16 02:56:17

Poniżej jest moją wersją głębokiego klonowania, obejmującą funkcje i z obsługą dla odniesień kołowych.

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

 4
Author: Radu Simionescu,
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-05 09:24:25