Mam obiekt, x. Chciałbym skopiować go jako obiekt y, takie, że zmiany w y nie modyfikują 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ż skopiuję jeden z własnych literalnych obiektów skonstruowanych.

Jak poprawnie sklonować obiekt JavaScript?

3420
soundly_typed 8 kwiecień 2009, 07:01

29 odpowiedzi

Najlepsza odpowiedź

Aby to zrobić, aby każdy obiekt w JavaScript nie będzie prosty ani prosty. Wpadniesz w problem błędnie zbierania atrybutów z prototypu obiektu, który należy pozostawić w prototypie i nie skopiowany do nowej instancji. Jeśli na przykład dodasz metodę clone do Object.prototype, ponieważ niektóre odpowiedzi przedstawione, będziesz musiał wyraźnie pomijać ten atrybut. Ale co jeśli są inne dodatkowe metody dodane do Object.prototype lub innych prototypów pośrednich, których nie wiesz? W takim przypadku nie powinieneś kopiować atrybuty, nie powinieneś, więc musisz wykryć nieprzewidziane, nielokalne atrybuty z hasOwnProperty Metoda.

Oprócz atrybutów niezlipaniowych napotkasz trudniejszy problem podczas próby kopiowania obiektów, które mają ukryte właściwości. Na przykład prototype jest ukrytą właściwością funkcji. Ponadto prototyp obiektu odnosi się również do atrybutu __proto__, który jest również ukryty i nie zostanie skopiowany przez a do / w pętli pętli iteracyjnej nad atrybuty obiektu źródłowego. Myślę, że __proto__ może być specyficzna dla tłumacza JavaScript Firefox i może to być coś innego w innych przeglądarkach, ale otrzymujesz zdjęcie. Nie wszystko jest nieocenialne. Możesz skopiować ukryty atrybut, jeśli znasz jego nazwę, ale nie wiem o żadnym sposobie, aby odkryć go automatycznie.

Jeszcze kolejne żarło w poszukiwaniu eleganckim rozwiązaniem jest poprawnie ustawianie dziedziczenia prototypu. Jeśli prototyp Twojego obiektu źródłowego jest Object, a następnie tworzenie nowego obiektu ogólnego z {X1}} będzie działać, ale jeśli prototyp źródła jest jakiś potomek Object, a następnie brakuje ci Dodatkowi członkowie z tego prototypu, który pominąłeś za pomocą filtra hasOwnProperty, lub które były w prototypie, ale w pierwszej kolejności nie były nielicealne. Jednym rozwiązaniem może być wywołanie właściwości obiektu źródłowego {X4}}, aby uzyskać początkowy obiekt kopiowania, a następnie skopiuj na atrybuty, ale wtedy nadal nie otrzymasz niezlipaniowych atrybutów. 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);

String daty dla d1 będzie 5 sekund za d2. Sposób, aby zrobić jeden Date tak samo, jak inny jest wywoływanie metody setTime, ale jest specyficzne dla klasy Date. Nie sądzę, że istnieje rozwiązanie ogólne przeciwko temu problemowi, choć chętnie chętnie się mylił!

Kiedy musiałem wdrożyć General Deep Kopiowanie, skończyło się na kompromisie, zakładając, że muszę tylko skopiować zwykłą Object, Array, Date, {x3}}, String, String }} lub Boolean. Ostatnie 3 typy są niezmienne, więc mogłem wykonać płytką kopię i nie martwić się o to zmianie. Objętywałem ponadto, że wszelkie elementy zawarte w Object lub Array byłyby również jednym z 6 prostych typów na tej liście. Można to osiągnąć za pomocą kodu takiego:

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 tablic tworzą strukturę drzewa. Oznacza to, że nie ma więcej niż jednego odniesienia do tych samych danych w obiekcie. 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 poradzić sobie z żadnym obiektem JavaScript, ale może być wystarczający dla wielu celów, o ile nie zakładasz, że po prostu będzie działać na wszystko, co wrzuciłeś.

1627
A. Levy 14 sierpień 2018, 15:12

Właśnie chciałem dodać do wszystkich rozwiązań Object.create w tym poście, że nie działa w pożądanym sposób z NodeJs.

W Firefoksie wynik

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

Jest

{test:"test"}.

W NodeJs to jest

{}
6
heinob 3 czerwiec 2012, 09:29
function clone(src, deep) {

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

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

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

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

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

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

    }

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

    return ret;
};
6
Bo Persson 31 lipiec 2012, 20:53

Ponieważ MineAvor stwierdził, że obiekt, który ma być klonowany, jest obiektem "Literal skonstruowany", rozwiązanie może być po prostu Generuj wiele razy, a nie klonowanie instancji obiektu:

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

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
6
Community 23 maj 2017, 12:34

Napisałem własną implementację. Nie jestem pewien, 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
e-cloud 29 lipiec 2016, 16:32

Jeśli nie używasz Date s, funkcji, niezdefiniowanego, regexp lub nieskończoności w obiekcie, bardzo prosta liniowiec jest JSON.parse(JSON.stringify(object)):

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

Działa to dla wszystkich obiektów zawierających obiekty, tablice, ciągi, logiczne i cyfry.

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

1025
grobouDu06 4 grudzień 2019, 15:51

Z jQuery możesz płytką kopię z Extion:

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

Kolejne zmiany w copiedObject nie wpłyną na originalObject i odwrotnie.

Lub zrobić głęboka kopia :

var copiedObject = jQuery.extend(true, {}, originalObject)
796
Burak 12 lipiec 2019, 13:45

W ECMASCRESS 6 jest Obiekt.Sign Metoda, które kopiuje Wartości wszystkich wymiennych właściwości własnych z jednego obiektu do drugiego. Na przykład:

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

Ale bądź świadomy, że zagnieżdżone obiekty są nadal skopiowane jako odniesienie.

729
Slava Babin 21 październik 2016, 11:14

Na MDN:

  • Jeśli chcesz płytkiej kopii, użyj Object.assign({}, a)
  • Dla "głębokiej" kopii, użyj JSON.parse(JSON.stringify(a))

Nie ma potrzeby bibliotek zewnętrznych, ale musisz sprawdzić Kompatybilność przeglądarki Najpierw.

263
Efren 8 marzec 2018, 07:42

Jest wiele odpowiedzi, ale żaden, co wspomina obiektu.Create z ECMAScript 5, co wprawdzie nie podaje dokładnej kopii, ale ustawia źródło jako prototyp nowego obiektu.

W ten sposób nie jest to dokładna odpowiedź na pytanie, ale jest to rozwiązanie jednolite, a tym samym elegancki. I działa najlepiej dla 2 przypadków:

  1. Gdzie takie dziedzictwo jest przydatne (DUH!)
  2. Gdzie obiekt źródłowy nie zostanie zmodyfikowany, co powoduje, że relacja między 2 obiektami bez problemu.

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? To rodzimy, bez zapętlenia, bez rekurencji. Jednak starsze przeglądarki będą potrzebować posiłku.

136
itpastorn 19 marzec 2012, 15:17

Istnieje kilka problemów z większością rozwiązań w Internecie. Postanowiłem więc dokonać następującego, co obejmuje, dlaczego przyjęta odpowiedź nie powinna być akceptowana.

Sytuacja rozpoczęcia

Chcę głębokiej kopii javascript Object z wszystkimi jej dziećmi i ich dziećmi i tak dalej. Ale ponieważ nie jestem rodzajem zwykłego dewelopera, mój {x1}} ma normalny normalny properties, circular structures, a nawet nested objects.

Utwórzmy więc najpierw circular structure i {x1}}}.

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

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

Przynieśmy wszystko razem 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 {x1}} i mutację go.

var b = a;

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

Wiesz, co się stało tutaj, ponieważ nie, nawet nie wylądujesz 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"
    }
}

Teraz znajdźmy rozwiązanie.

JSON

Pierwsza próba, której próbowałem, używa JSON.

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

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

Nie marnuj zbyt dużo czasu, otrzymasz TypeError: Converting circular structure to JSON.

Rekurencyjna kopia (zaakceptowana "odpowiedź")

Spójrzmy na zaakceptowaną odpowiedź.

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, jak Date, ale to nie było wymóg.

var b = cloneSO(a);

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

Rekursja i {x0}} nie działa dobrze razem ... RangeError: Maximum call stack size exceeded

Natywne rozwiązanie

Po kłótnie z moim współpracownikiem mój szef zapytał nas, co się stało, a on znalazł proste roztwór po kilku Googlingu. 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 uchwyty 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, nie działa z zagnieżdżoną strukturą wewnątrz.

Polyfill dla natywnego rozwiązania

W starszej przeglądarce jest Object.create w starszej przeglądarce, podobnie jak tj. To jest coś takiego jak polecane przez Mozillę, i oczywiście nie jest idealne i powoduje to samo problem, co natywne 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ść F poza zakresem, dzięki czemu możemy spojrzeć na to, co mówi nam instanceof.

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ę gorsze wyjście.

Im lepszy (ale nie doskonały) rozwiązanie

Podczas kopania, znalazłem podobne pytanie (W JavaScript, podczas wykonywania głębokiej kopii, jak uniknąć cyklu, ze względu na nieruchomość" To "?) do tego, ale w taki sposób Lepsze 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 zmianę instance nested i circ do Object.

Struktura drzew, które dzielą liść nie zostanie skopiowany, staną się dwoma niezależnymi liśćmi:

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

Wniosek

Ostatnie rozwiązanie przy użyciu rekurencji i pamięci podręcznej, może nie być najlepszy, ale jest to real głęboka kopia obiektu. Uchwytuje proste properties, circular structures i nested object, ale będzie zepsuć instancję ich podczas klonowania.

jsfiddle

91
tgogos 1 listopad 2018, 08:36

Jeśli jesteś w porządku z płytką kopią, biblioteka underscore.js ma a Clone Metoda.

y = _.clone(x);

Lub możesz go przedłużyć

copiedObject = _.extend({},originalObject);
79
Nadeem Yasin 19 styczeń 2015, 18:47

ok, Wyobraź sobie, że masz ten obiekt, a chcesz go klonować:

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

Lub

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

Odpowiedź zależy głównie od tego, na jakim ECMASCREPT , używasz, w ES6+, możesz po prostu użyć Object.assign, aby zrobić klon:

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

Lub używając operatora rozprzestrzeniania 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żego kawałka danych do kopiowania, ale może to być jedna linia poręczna droga w wielu przypadkach , 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
70
Alireza 14 marzec 2019, 09:59

Jednym, szczególnie nienagannym rozwiązaniem jest użycie kodowania JSON, aby dokonać głębokich kopii obiektów, które nie mają metod członków. Metodologia jest do JSON kodować obiekt docelowy, a następnie, dekodowanie go, otrzymasz kopię, której szukasz. Możesz dekodować tyle razy, ile chcesz mieć jak najwięcej kopii.

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

Metodologia ta była idealna dla mojego przypadku użycia, ponieważ przechowuję Blobs JSON w sklepie z wartościami kluczowymi, a gdy są narażone jako obiekty w API JavaScript, każdy obiekt faktycznie zawiera kopię oryginalnego stanu obiektu, więc my Może obliczyć deltę po tym, jak dzwoniący zmutował odsłonięty 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
44
Tim 28 październik 2016, 17:24

Możesz po prostu użyć Spread Neight, aby skopiować obiekt bez referencji. Ale bądź ostrożny (patrz komentarze), "Kopiuj" jest tylko na najniższym poziomie obiektu / tablicy. Nested Properties 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 natywnych klonów głębokich klonów. Użyj funkcji użytkowej. Na przykład ramda:

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

36
musemind 5 kwiecień 2017, 12:30

Dla tych, którzy korzystają z Angularjs, istnieje również bezpośrednia metoda klonowania lub rozszerzenia obiektów w tej bibliotece.

var destination = angular.copy(source);

Lub

angular.copy(source, destination);

Więcej w kątach.copy Dokumentacja...

27
Lukas Jelinek 3 wrzesień 2014, 19:08

Z tego artykułu: Jak skopiować tablice i obiekty w JavaScript przez 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;
};
25
The Red Pea 10 marzec 2020, 19:43

Odpowiedź A.levy jest prawie kompletna, oto mój mały wkład: Jest sposób, jak obsługiwać rekurencyjne odniesienia , zobacz tę linię

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

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

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

Zainspirowany wyczerpującym badaniem A.levy i podejście prototypujące Calvina, oferuję to 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 Burke w odpowiedzi.

24
Jan Turoň 2 grudzień 2012, 20:49

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;
}
23
Dan D. 23 luty 2012, 20:04

W ECMASCRIPT 2018.

let objClone = { ...obj };

Bądź świadomy, że zagnieżdżone obiekty są nadal kopiowane jako odniesienie.

23
Aliaksandr Sushkevich 3 sierpień 2018, 16:16

W ES-6 możesz po prostu użyć obiektu.Asign (...). Dawny:

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

Dobre odniesienie jest tutaj: https://googlechrome.github.io/samples/object-Assign-es6/9/ A >.

20
João Oliveira 13 kwiecień 2017, 20:17

Korzystanie z Lodash:

var y = _.clone(x, true);
19
VaZaA 13 grudzień 2012, 00:05

Możesz sklonować obiekt i usunąć dowolne odniesienie z poprzedniego przy użyciu jednej linii kodu. Po prostu zrobić:

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

W przypadku przeglądarek / silników, które obecnie nie obsługują obiektu.Create, że 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
Community 16 wrzesień 2012, 05:27

Nowa odpowiedź na stare pytanie! Jeśli masz przyjemność wykorzystać ECMAScript 2016 (ES6) z Składnia rozprzestrzeniania, to łatwe.

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

Zapewnia to czystą metodę płytkiej kopii obiektu. Dokonując głębokiej kopii, co oznacza MAKIGN Nowa kopia każdej wartości w każdym rekrusywnie zagnieżdżonym obiekcie, wymaga włączenia cięższych roztworów powyżej.

JavaScript prowadzi do ewolucji.

13
Charles Merriam 16 grudzień 2016, 11:34
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

Rozwiązanie ES6 Jeśli chcesz (płytkie) klonować instancja klasy , a nie tylko obiekt właściwości.

13
flori 27 czerwiec 2017, 12:57

Dla głębokiej kopii i klonu, JSON.Stringify, a następnie JSON.PARSE obiekt:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
12
Jesse Reza Khorasanee 28 czerwiec 2019, 04:06

Myślę, że istnieje prosta i odpowiedzialna odpowiedź. W głębokim kopiowaniu istnieją dwa wątpliwości:

  1. Utrzymywać właściwości niezależne od siebie.
  2. I utrzymuj metody przy życiu na sklonowanym obiekcie.

Więc myślę, że jednym prostym rozwiązaniem będzie pierwsze serializację i deserializację, a następnie wykonać przypisanie go do funkcji kopiowania.

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

Chociaż to pytanie ma wiele odpowiedzi, mam nadzieję, że ten pomoże.

11
ConductedClever 11 czerwiec 2019, 08:57

Jest to adaptacja Kodeksu A. Levya, aby również obsługiwać klonowanie funkcji i odniesień wielokrotnych / cyklicznych - co oznacza, że jeśli są to, że jeśli klonowane są dwie właściwości w drzewie, są odniesieniem tego samego obiektu, klonowane drzewo obiektu będzie je Właściwości wskazują na jeden i ten sam klon odwołanego obiektu. Rozwiązuje to również przypadek cyklicznych zależności, które, jeśli zostaną pozbawione, prowadzi do nieskończonej pętli. Złożoność algorytmu jest 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;
}

Kilka szybkich testów.

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));
7
Radu Simionescu 16 lipiec 2012, 09:23