Pytanie Jak poprawnie sklonować obiekt JavaScript?


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. Nie stanowi to problemu, ponieważ kopiuję jeden z moich własnych, dosłownie skonstruowanych obiektów.

Jak poprawnie sklonować obiekt JavaScript?


2403


pochodzenie


Zobacz to pytanie: stackoverflow.com/questions/122102/... - Niyaz
W przypadku JSON używam mObj=JSON.parse(JSON.stringify(jsonObject)); - Lord Loh.
Naprawdę nie rozumiem, dlaczego nikt nie sugeruje Object.create(o), robi wszystko, o co prosi autor? - froginvasion
var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;  Po wykonaniu tego y.deep.key będzie również 2, a więc Object.create NIE MOŻNA UŻYWAĆ do klonowania ... - Ruben Stolk
@ r3wt, który nie zadziała ... Proszę pisać tylko po wykonaniu podstawowego testu rozwiązania .. - akshay


Odpowiedzi:


Zaktualizowana odpowiedź

Po prostu użyj Object.assign () jak proponowano tutaj

Ale pamiętaj, że to tylko płytka kopia. Zagnieżdżone obiekty są nadal kopiowane jako odniesienia.


Nieaktualna odpowiedź

Wykonanie tego dla dowolnego obiektu w JavaScript nie będzie proste ani bezpośrednie. Pojawi się problem błędnego pobrania atrybutów z prototypu obiektu, który powinien pozostać w prototypie i nie powinien być kopiowany do nowej instancji. Jeśli na przykład dodajesz clone metoda do Object.prototype, jak pokazują niektóre odpowiedzi, musisz wyraźnie pominąć ten atrybut. Ale co, jeśli do tego dodamy inne dodatkowe metody Object.prototypelub inne pośrednie prototypy, o których nie wiesz? W takim przypadku skopiujesz atrybuty, których nie powinieneś, więc musisz wykryć nieprzewidziane, nielokalne atrybuty przy pomocy hasOwnProperty metoda.

Oprócz atrybutów nieprzeliczalnych napotkasz trudniejszy problem podczas kopiowania obiektów, które mają ukryte właściwości. Na przykład, prototype jest ukrytą własnością funkcji. Do atrybutu odwołuje się także prototyp obiektu __proto__, który również jest ukryty i nie zostanie skopiowany przez iterację pętli for / in nad atrybutami obiektu źródłowego. Myślę __proto__ może być charakterystyczne dla interpretera JavaScript w Firefoksie i może być czymś innym w innych przeglądarkach, ale dostajesz obrazek. Nie wszystko jest przeliczalne. Możesz skopiować ukryty atrybut, jeśli znasz jego nazwę, ale nie znam żadnego sposobu na jego automatyczne wykrycie.

Kolejnym problemem w poszukiwaniach eleganckiego rozwiązania jest poprawne skonfigurowanie dziedziczenia prototypów. Jeśli masz prototyp obiektu źródłowego Object, po prostu tworząc nowy ogólny obiekt za pomocą {} zadziała, ale jeśli prototypem źródła jest jakiś potomek Object, wtedy będziesz brakować dodatkowych członków z tego prototypu, który pominąłeś używając hasOwnProperty filtr, lub które były w prototypie, ale nie były w ogóle przeliczalne. Jednym rozwiązaniem może być wywołanie obiektu źródłowego constructor Właściwość, aby uzyskać początkowy obiekt kopiowania, a następnie skopiować atrybuty, ale nadal nie uzyska się atrybutów nieprzeliczalnych. Na przykład a Date obiekt przechowuje dane jako ukryty element:

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

Łańcuch daty dla d1 będzie o 5 sekund opóźniony d2. Sposób na zrobienie tego Date taki sam jak inny to wywołanie setTime metoda, ale jest to specyficzne dla Date klasa. Nie sądzę, że istnieje ogólne rozwiązanie tego problemu, choć z chęcią się mylę!

Kiedy musiałem wdrożyć ogólne, głębokie kopiowanie, doszedłem do kompromisu, zakładając, że wystarczy skopiować zwykły Object, Array, Date, String, Number, lub Boolean. Ostatnie 3 typy są niezmienne, więc mogłem wykonać płytką kopię i nie martwić się o jej zmianę. Ponadto założyłem, że wszelkie elementy zawarte w Object lub Array byłby również jednym z 6 prostych typów z tej listy. Można to osiągnąć za pomocą kodu podobnego do następującego:

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ć poprawnie dla 6 prostych typów, o których wspomniałem, o ile dane w obiektach i tablicach tworzą strukturę drzewa. Oznacza to, że nie ma więcej niż jedno odniesienie 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 obsłużyć żadnego obiektu JavaScript, ale może być wystarczający dla wielu celów, o ile nie zakładasz, że będzie on działał tylko dla wszystkiego, co na niego rzucisz.


1313



prawie działało dobrze w nodejs - po prostu musiałem zmienić linię dla (var i = 0, var len = obj.length; i <len; ++ i) {to for (var i = 0; i <obj.length; + + i) { - Trindaz
Dla przyszłych użytkowników google: ta sama głęboka kopia, przekazująca odwołania rekurencyjnie zamiast za pomocą instrukcji "return" na stronie gist.github.com/2234277 - Trindaz
W dzisiejszych czasach JSON.parse(JSON.stringify([some object]),[some revirer function]) być rozwiązaniem? - KooiInc
W pierwszym fragmencie, czy jesteś pewien, że tak nie powinno być var cpy = new obj.constructor()? - cyon
Przypisanie obiektów nie wydaje się być prawdziwą kopią w Chrome, zachowuje odwołanie do oryginalnego obiektu - skończyło się na użyciu JSON.stringify i JSON.parse do klonowania - działało idealnie - 1owk3y


Z jQuery możesz płytka kopia z poszerzać:

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

kolejne zmiany obiektu skopiowanego nie wpłyną na obiekt oryginalny i na odwrót.

Lub zrobić głęboka kopia:

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

713



lub nawet: var copiedObject = jQuery.extend({},originalObject); - Grant McLean
Przydaje się również, aby podać true jako pierwszy parametr dla głębokiej kopii: jQuery.extend(true, {}, originalObject); - Will Shaver
Tak, ten link był dla mnie pomocny (to samo rozwiązanie co Pascal) stackoverflow.com/questions/122102/... - Garry English
@Will Shaver - TAK! To jest to! Bez opcji głębokiej kopii to nie działa dla mnie! - thorinkor
Tylko uwaga, to nie kopiuje proto konstruktor oryginalnego obiektu - Sam Jones


Jeśli nie używasz funkcji wewnątrz obiektu, bardzo prosta jedna linijka może być następująca:

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

Działa to dla wszystkich obiektów zawierających obiekty, tablice, łańcuchy, wartości logiczne i liczby.

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


692



Zauważ, że można to wykorzystać tylko do testowania. Po pierwsze, jest daleki od optymalnego pod względem czasu i zużycia pamięci. Po drugie, nie wszystkie przeglądarki mają takie metody. - Nux
@Nux, dlaczego nie optymalna pod względem czasu i pamięci? MiJyn mówi: "Powodem, dla którego ta metoda jest wolniejsza od płytkiego kopiowania (na głębokim obiekcie), jest to, z definicji, głęboka kopia, ale ponieważ JSON jest implementowany w natywnym kodzie (w większości przeglądarek), będzie to znacznie szybsze niż użycie jakiegokolwiek innego rozwiązania do kopiowania w oparciu o javascript, a czasami może być szybsze niż technika płytkiego kopiowania w javascript (patrz: jsperf.com/cloning-an-object/79). " stackoverflow.com/questions/122102/... - BeauCielBleu
Chcę tylko dodać do tego aktualizację na październik 2014. Chrome 37+ jest szybszy z JSON.parse (JSON.stringify (oldObject)); Zaletą korzystania z tego jest to, że silnik javascript jest bardzo łatwy w przeglądaniu i optymalizacji w coś lepszego, jeśli chce. - mirhagk
Byłoby to skazane na cały JSON, gdyby obiekt nie był zdolny do ciągłego udoskonalania takich rzeczy jak Nieskończoność, niezdefiniowanyitp. Wypróbuj ten obiekt: a = { b: Infinity, c: undefined } - kumar_harsh
Aktualizacja 2016: Powinno to teraz działać niemal we wszystkich powszechnie używanych przeglądarkach. (widzieć Mogę uzyć...) Głównym pytaniem będzie teraz, czy jest wystarczająco wydajne. - James Foster


W ECMAScript 6 jest Object.assign metoda, która kopiuje wartości wszystkich przeliczalnych właściwości z jednego obiektu do drugiego. Na przykład:

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

Należy jednak pamiętać, że zagnieżdżone obiekty są nadal kopiowane jako odniesienie.


509



Tak, wierzę w to Object.assign jest drogą do zrobienia. Łatwo też ją polyfilować: gist.github.com/rafaelrinaldi/43813e707970bd2d77fa - Rafael
Ale pamiętaj, że to tylko płytka kopia. Zagnieżdżone obiekty są nadal kopiowane jako referencje! - ohager
Należy również pamiętać, że skopiuje on "metody" zdefiniowane za pomocą literałów obiektów (ponieważ one są przeliczalne), ale nie metody wymierzone przez mechanizm "klasy" (ponieważ te nie są przeliczalny). - Marcus Junius Brutus
Myślę, że należy wspomnieć, że nie jest to wspierane przez IE z wyjątkiem Edge. Niektórzy ludzie nadal używają tego. - Saulius
To jest to samo, co @EugeneTiurin jego odpowiedź. - Wilt


Istnieje wiele odpowiedzi, ale żadna z nich nie wspomina Object.create z ECMAScript 5, który wprawdzie nie podaje dokładnej kopii, ale ustawia źródło jako prototyp nowego obiektu.

Tak więc nie jest to dokładna odpowiedź na pytanie, ale jest to rozwiązanie jednoliniowe, a przez to eleganckie. I działa najlepiej w 2 przypadkach:

  1. Gdzie takie dziedzictwo jest przydatne (duh!)
  2. Gdzie obiekt źródłowy nie zostanie zmodyfikowany, co sprawia, że ​​relacja między 2 obiektami nie jest problemem.

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, a więc nie ma pętli ani rekurencji. Jednak starsze przeglądarki będą potrzebować folii polyfill.


114



Uwaga: Object.create nie jest głęboką kopią (jak wspomniano, nie ma rekursji). Dowód: var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */; - zamnuts
To jest prototypowe dziedziczenie, a nie klonowanie. To są zupełnie różne rzeczy. Nowy obiekt nie ma żadnych własnych właściwości, tylko wskazuje na właściwości prototypu. Celem klonowania jest stworzenie nowego, świeżego obiektu, który nie odwołuje się do żadnych właściwości w innym obiekcie. - d13
Całkowicie się z tobą zgadzam. Zgadzam się również, że nie jest to klonowanie, które mogłoby być "zamierzone". Ale przyjdź na ludzi, przyjmij naturę JavaScript zamiast próbować znaleźć niejasne rozwiązania, które nie są ustandaryzowane. Pewnie, nie lubisz prototypów i wszystkie one są dla Ciebie "blahowe", ale w rzeczywistości są bardzo przydatne, jeśli wiesz, co robisz. - froginvasion
@RobG: W tym artykule wyjaśniono różnicę między odwoływaniem a klonowaniem: en.wikipedia.org/wiki/Cloning_(programming). Object.create wskazuje na właściwości rodzica poprzez referencje. Oznacza to, że jeśli wartości właściwości rodzica ulegną zmianie, zmieni się również jego wartość. Ma to pewne zaskakujące efekty uboczne z zagnieżdżonymi tablicami i obiektami, które mogą prowadzić do trudnych do znalezienia błędów w kodzie, jeśli ich nie znasz: jsbin.com/EKivInO/2. Sklonowany obiekt to zupełnie nowy, niezależny obiekt, który ma takie same właściwości i wartości, jak obiekt nadrzędny, ale nie jest połączony z obiektem nadrzędnym. - d13
To wprowadza w błąd ludzi ... Object.create () może być używany jako środek do dziedziczenia, ale klonowanie nie jest do niego podobne. - prajnavantha


Elegancki sposób klonowania obiektu JavaScript w jednym wierszu kodu

Na Object.assign Metoda jest częścią standardu ECMAScript 2015 (ES6) i spełnia dokładnie to, czego potrzebujesz.

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

Metoda Object.assign () służy do kopiowania wartości wszystkich przeliczalnych właściwości 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;
    }
  });
}

105



Przepraszam za głupie pytanie, ale dlaczego tak Object.assign weź dwa parametry, gdy value funkcja w polyfill zajmuje tylko jeden parametr? - Qwertie
@Qwertie wczoraj Wszystkie argumenty są iterowane i łączone w jeden obiekt, przy czym pierwszeństwo mają właściwości z ostatniego zatwierdzonego argumentu - Eugene Tiurin
Och, rozumiem, dzięki (nie byłem zaznajomiony z arguments obiekt wcześniej.) Mam problem ze znalezieniem Object() przez Google ... to typografia, prawda? - Qwertie
to tylko wykona płytkie "klonowanie" - Marcus Junius Brutus


Za 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 zewnętrznych bibliotek, ale musisz sprawdzić najpierw kompatybilność z przeglądarką.


105



JSON.parse (JSON.stringify (a)) wygląda pięknie, ale przed jego użyciem zalecam testowanie czasu potrzebnego do pobrania. W zależności od wielkości obiektu opcja ta może nie być najszybsza. - Edza
Metoda JSON Zauważyłem, że konwertuje obiekty daty na łańcuchy, ale nie wraca do dat. Mieć do czynienia z zabawą stref czasowych w JavaScript i ręcznie naprawić wszelkie daty. Mogą to być podobne przypadki dla innych typów oprócz dat - Steve Seeger
dla Date, użyłbym pliku chwili.js, ponieważ ma clone funkcjonalność. zobacz więcej tutaj momentjs.com/docs/#/parsing/moment-clone - Tareq


Jeśli wszystko jest w porządku z płytką kopią, biblioteka underscore.js ma klonować metoda.

y = _.clone(x);

lub możesz go rozszerzyć jak

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

70



I lokaj ma cloneDeep - dule
Dzięki. Korzystając z tej techniki na serwerze Meteor. - Turbo
Merci, człowieku! to zrobiło dla mnie niesamowite wrażenie, że używam go od jakiegoś czasu. utknąłem w projekcie vue, o ile wiem, że vue nie ma wbudowanej funkcji klonowania obiektu takiego jak kątowy (angular.copy) - Arnaud Bouchot


Jest kilka problemów z większością rozwiązań w Internecie. Postanowiłem więc podjąć działania następcze, które obejmują, dlaczego przyjęta odpowiedź nie powinna zostać przyjęta.

sytuacja początkowa

chcę głęboka kopia JavaScript Object ze wszystkimi swoimi dziećmi i ich dziećmi i tak dalej. Ale ponieważ nie jestem normalnym programistą, mój Object ma normalna  properties, circular structures i nawet nested objects.

Stwórzmy więc circular structure i a nested object pierwszy.

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

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

Połączmy wszystko w jeden Object o imieniu a.

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

Następnie chcemy skopiować a do zmiennej o nazwie b i zmutuj to.

var b = a;

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

Wiesz, co się tutaj stało, bo gdybyś nie wylądował na to wspaniałe pytanie.

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

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

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

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

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

Kopia rekursywna (zaakceptowana "odpowiedź")

Rzućmy okiem 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 rekursywną kopią 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';

Rekursja i circular structures nie działa dobrze razem ... RangeError: Maximum call stack size exceeded

rodzime rozwiązanie

Po kłótni z moim współpracownikiem mój szef zapytał nas, co się stało, i znalazł prostą rozwiązanie po kilku googlach. To jest nazwane 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ło z zagnieżdżoną strukturą wewnątrz.

polyfill dla rozwiązania natywnego

Jest polyfill dla Object.create w starszej przeglądarce, podobnie jak IE 8. Jest to coś, co zaleca Mozilla, i oczywiście nie jest doskonałe i powoduje ten sam problem, co rodzime rozwiązanie.

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

var b = clonePF(a);

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

Położyłem F poza zasięgiem, abyśmy mogli rzucić okiem na to, co instanceof Powiedz nam.

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 rodzime rozwiązanie, ale nieco gorsza wydajność.

lepsze (ale nie doskonałe) rozwiązanie

Podczas kopania znalazłem podobne pytanie (Czy w JavaScript, wykonując głęboką kopię, należy unikać cyklu, ponieważ właściwość jest "tym"?) do tego, ale z lepszym rozwiązaniem.

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 wynik ...

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 do Object.

Struktura drzew dzielących liść nie zostanie skopiowana, staną się dwoma niezależnymi liśćmi:

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

wniosek

Ostatnie rozwiązanie wykorzystujące rekursję i pamięć podręczną może nie być najlepsze, ale jest to real głęboka kopia obiektu. Obsługuje proste properties, circular structures i nested object, ale zepsułoby to ich instancję podczas klonowania.

http://jsfiddle.net/einfallstoll/N4mr2/


63



więc powikłaniem jest uniknięcie tego problemu :) - mikus
@mikus, aż pojawi się real specyfikacja, która obejmuje więcej niż tylko podstawowe zastosowania, tak. - Fabio Poloni
Odpowiednia analiza rozwiązań przedstawionych powyżej, ale wniosek wyciągnięty przez autora wskazuje, że nie ma rozwiązania tego pytania. - Amir Mog
To wstyd, że JS nie zawiera natywnej funkcji klonowania. - l00k
Spośród wszystkich najważniejszych odpowiedzi, uważam, że jest on zbliżony do poprawnego. - KTU


Jednym ze szczególnie nieeleganckiego rozwiązania jest użycie kodowania JSON do tworzenia głębokich kopii obiektów, które nie mają metod składowych. Metodologia polega na kodowaniu obiektu docelowego przez JSON, a następnie poprzez dekodowanie 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ż przechowuję bloki JSON w magazynie klucz-wartość, a gdy są one odsłonięte jako obiekty w JavaScript API, każdy obiekt faktycznie zawiera kopię oryginalnego stanu obiektu, więc może obliczyć delta po tym, jak dzwonią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

36



Dlaczego funkcje nie należą do JSON? Widziałem, jak przekazywano je jako JSON więcej niż raz ... - the_drow
Funkcje nie są częścią specyfikacji JSON, ponieważ nie są bezpiecznym (lub inteligentnym) sposobem przesyłania danych, do czego służy JSON. Wiem, że natywny koder JSON w Firefoksie po prostu ignoruje funkcje do niego przekazywane, ale nie jestem pewien co do zachowania innych. - Kris Walker
IMHO najlepszą odpowiedzią, ponieważ PO stwierdza "obiekty skonstruowane dosłownie" - mark
Nie ma nic bardziej nieeleganckiego niż użycie słowa nieeleganckiego. - Kevin Laity
@znak: { 'foo': function() { return 1; } } jest obiektem skonstruowanym dosłownie. - abarnert