Klonowanie obiektu w Node.js


203

Jaki jest najlepszy sposób klonowania obiektu w node.js

np. chcę uniknąć sytuacji, w której:

var obj1 = {x: 5, y:5};
var obj2 = obj1;
obj2.x = 6;
console.log(obj1.x); // logs 6

Obiekt może zawierać złożone typy jako atrybuty, więc proste dla (var x w obj1) nie rozwiązałoby się. Czy muszę sam napisać rekurencyjny klon, czy jest coś wbudowanego, czego nie widzę?


23
1. npm install underscore2. var _ = require('underscore')3 _.clone(objToClone).;
Salman von Abbas

4
Zauważ, że w powyższym komentarzu @ SalmanPK jest to płytki klon. więc będzie działać na przykładzie slifty, ale jeśli istnieją zagnieżdżone tablice lub obiekty, będą to odniesienia. : /
Jesse

1
Uważam, że ten artykuł jest bardzo pomocny: heyjavascript.com/4-creative-ways-to-clone-objects
Jordan Hudson

3
@Jordan Hudson - Bardzo miłe użycie JSON w drugim przykładzie. var newObj = JSON.parse (JSON.stringify (oldObj)); // Teraz newObj jest klonem. Jedynym problemem jest to, że stringify nie będzie działać na odwołaniu rekurencyjnym, więc należy zachować ostrożność.
Kfir Erez

Odpowiedzi:


298

Możliwość 1

Głęboka kopia o niskiej falbance:

var obj2 = JSON.parse(JSON.stringify(obj1));

Możliwość 2 (przestarzałe)

Uwaga: To rozwiązanie jest teraz oznaczone jako przestarzałe w dokumentacji Node.js :

Metoda util._extend () nigdy nie była przeznaczona do użycia poza wewnętrznymi modułami Node.js. Społeczność i tak go znalazła i wykorzystała.

Jest przestarzały i nie powinien być używany w nowym kodzie. JavaScript ma bardzo podobne wbudowane funkcje dzięki Object.assign ().

Oryginalna odpowiedź: :

Aby uzyskać płytką kopię, użyj wbudowanej util._extend()funkcji Node .

var extend = require('util')._extend;

var obj1 = {x: 5, y:5};
var obj2 = extend({}, obj1);
obj2.x = 6;
console.log(obj1.x); // still logs 5

Kod źródłowy funkcji Node znajduje _extendsię tutaj: https://github.com/joyent/node/blob/master/lib/util.js

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || typeof add !== 'object') return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

5
Pytanie konkretnie wymagało klonowania rekurencyjnego. To jest płytki klon.
Benjamin Atkin

28
Czy nazwa nie _*powinna oznaczać, że jest to metoda prywatna i na której nie można polegać?
Puszysty

7
Każdy projekt JavaScript dowolnej wielkości ma jedną lub więcej implementacji ext (), a Węzeł nie jest wyjątkiem. Rdzeń Node.js szeroko wykorzystuje tę funkcję. Cytując Izaaka: „Niedługo nigdzie się to nie zmierza”.
jimbo

2
działało dla mnie idealnie. znacznie lepiej niż bawić się prototypem Object imo
Michael Dausmann

12
To jest ZŁA odpowiedź. Zgodnie z dokumentacją węzła: nodejs.org/api/util.html#util_util_extend_obj metoda nigdy nie był przeznaczony do stosowania na zewnątrz wewnętrznych modułów node.js. Społeczność i tak go znalazła i wykorzystała. Jest przestarzały i nie powinien być używany w nowym kodzie. JavaScript ma bardzo podobną wbudowaną funkcjonalność poprzezutil._extend() Object.assign().
Jordie

265

Dziwię się, Object.assignże nie wspomniano.

let cloned = Object.assign({}, source);

Jeśli jest dostępny (np. Babel), możesz użyć operatora rozkładania obiektów :

let cloned = { ... source };

1
uratowałeś mi dzień! Dzięki
wzr1337,

2
jest to znacznie lepsze rozwiązanie niż zaimportowanie biblioteki innej firmy lub użycie niedoskonałego obejścia JSON. Dzięki!
Neil S

75
to jest płytka kopia
Jordan Davidson

14
Ostrzeżenie dla głębokiego klonowania, w przypadku głębokiego klonowania nadal należy używać innych alternatyw. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
gsalgadotoledo

1
Z tego, co mogę powiedzieć, operator rozkładania obiektów nie jest rzeczą ES6, ale propozycją etapu 3. co oznacza, że ​​możesz go używać z babelem, ale nie bez tego, co rozumiem. github.com/tc39/…
macdja38

24
Object.defineProperty(Object.prototype, "extend", {
    enumerable: false,
    value: function(from) {
        var props = Object.getOwnPropertyNames(from);
        var dest = this;
        props.forEach(function(name) {
            if (name in dest) {
                var destination = Object.getOwnPropertyDescriptor(from, name);
                Object.defineProperty(dest, name, destination);
            }
        });
        return this;
    }
});

Spowoduje to zdefiniowanie metody rozszerzania, której można użyć. Kod pochodzi z tego artykułu.


Nie rozumiem, jak to ma działać. Zmienia oryginalny obiekt! Jak mam użyć tej funkcji, aby uzyskać klon obiektu? Czy możesz tutaj dodać kod użytkowania? Po przeczytaniu Twojego postu i postu na blogu nadal nie mogę zrozumieć, w jaki sposób ma on służyć do klonowania obiektu.
Brad

3
czy to naprawdę działa? „if (name w dest)” - zmieni właściwość tylko, jeśli już istnieje w dest. należy to zanegować.
memical

8
Czy modyfikowanie Object.prototype nie powinno być słowne? Również ten link do artykułu jest zepsuty.
Daniel Schaffer,

Właśnie wypróbowałem link do artykułu i działa dla mnie. Być może był to blip sieciowy, gdy próbowałeś.
Michael Dillon,

W oparciu o szereg komentarzy zaktualizowałem odpowiedź, aby uwzględnić wariant, który nie dodaje się do prototypu obiektu.
Shamasis Bhattacharya

20
var obj2 = JSON.parse(JSON.stringify(obj1));

2
Zostało to już zasugerowane w tej istniejącej odpowiedzi, nie ma sensu go powtarzać.
Shadow Wizard is Ear For You

@ShadowWizard są to różne metody. Ten po prostu konwertuje na json i wraca do obiektu, podczas gdy połączona odpowiedź używa Object.keys()do iteracji po obiekcie
mente

Ta odpowiedź jest zła. JSON.stringify pobiera obiekt daty i redukuje go do ciągu, a następnie po analizie pozostawia go jako ciąg, aby mutować stan obiektu, pozostawiając ci inny obiekt niż początkowo w przypadku dat.
twboc



8

Istnieje kilka modułów Node, jeśli nie chcesz „tworzyć własnych”. Ten wygląda dobrze: https://www.npmjs.com/package/clone

Wygląda na to, że obsługuje wszystkie rodzaje rzeczy, w tym odwołania cykliczne. Ze strony github :

klonuj wzorce klonując obiekty, tablice, obiekty Data i obiekty RegEx. Wszystko jest klonowane rekurencyjnie, dzięki czemu można na przykład klonować daty w tablicach w obiektach. [...] Referencje okólne? Tak!


7

Ten kod działa również, ponieważ metoda Object.create () tworzy nowy obiekt z określonym obiektem prototypowym i właściwościami.

var obj1 = {x:5, y:5};

var obj2 = Object.create(obj1);

obj2.x; //5
obj2.x = 6;
obj2.x; //6

obj1.x; //5

4
jest to płytka kopia
Radagast the Brown

6

Najprostszym i najszybszym sposobem klonowania obiektu w NodeJS jest użycie metody Object.keys (obj)

var a = {"a": "a11", "b": "avc"};
var b;

for(var keys = Object.keys(a), l = keys.length; l; --l)
{
   b[ keys[l-1] ] = a[ keys[l-1] ];
}
b.a = 0;

console.log("a: " + JSON.stringify(a)); // LOG: a: {"a":"a11","b":"avc"} 
console.log("b: " + JSON.stringify(b)); // LOG: b: {"a":0,"b":"avc"}

Metoda Object.keys wymaga JavaScript 1.8.5; nodeJS v0.4.11 obsługuje tę metodę

ale oczywiście w przypadku obiektów zagnieżdżonych należy zaimplementować funkcję rekurencyjną


Innym rozwiązaniem jest użycie natywnego JSON (zaimplementowanego w JavaScript 1.7), ale jest on znacznie wolniejszy (~ 10 razy wolniejszy) niż poprzedni

var a = {"a": i, "b": i*i};
var b = JSON.parse(JSON.stringify(a));
b.a = 0;

5

Istnieje również projekt na Github, który ma być bardziej bezpośrednim portem jQuery.extend():

https://github.com/dreamerslab/node.extend

Przykład zmodyfikowany z dokumentów jQuery :

var extend = require('node.extend');

var object1 = {
    apple: 0,
    banana: {
        weight: 52,
        price: 100
    },
    cherry: 97
};

var object2 = {
    banana: {
        price: 200
    },
    durian: 100
};

var merged = extend(object1, object2);


4

Wszyscy cierpicie, ale rozwiązanie jest proste.

var obj1 = {x: 5, y:5};

var obj2 = {...obj1}; // Bum


3

Szukając prawdziwej opcji klonowania, natknąłem się na link Ridcully do tutaj:

http://my.opera.com/GreyWyvern/blog/show.dml/1725165

Zmodyfikowałem rozwiązanie na tej stronie, aby funkcja dołączona do Objectprototypu nie była wymienna. Oto mój wynik:

Object.defineProperty(Object.prototype, 'clone', {
    enumerable: false,
    value: function() {
        var newObj = (this instanceof Array) ? [] : {};
        for (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;
    }
});

Mam nadzieję, że pomoże to również komuś innemu. Należy pamiętać, że istnieją pewne zastrzeżenia ... szczególnie w przypadku właściwości o nazwie „klon”. To działa dobrze dla mnie. Nie biorę żadnego uznania za napisanie tego. Ponownie zmieniłem tylko sposób, w jaki był definiowany.


To jest źle. Typ daty to obiekt, więc ten kod zastąpiłby daty pustymi obiektami ... Nie używaj tego.
jtblin,


0

Jeśli używasz skryptu do kawy, jest to tak proste, jak:

newObject = {}
newObject[key] = value  for own key,value of oldObject

Chociaż to nie jest głęboki klon.



0

Nie ma wbudowanego sposobu wykonania prawdziwego klonowania (głębokiej kopii) obiektu w node.js. Istnieje kilka trudnych przypadków, więc zdecydowanie powinieneś użyć do tego biblioteki. Taką funkcję napisałem dla mojej biblioteki simpleoo . Możesz użyć tej deepCopyfunkcji bez użycia czegokolwiek z biblioteki (która jest dość mała), jeśli jej nie potrzebujesz. Ta funkcja obsługuje klonowanie wielu typów danych, w tym tablic, dat i wyrażeń regularnych, obsługuje odwołania rekurencyjne, a także działa z obiektami, których funkcje konstruktora mają wymagane parametry.

Oto kod:

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

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

    // Initialize the visited objects array if needed
    // This is used to detect cyclic references
    if (_visited == undefined){
        _visited = [];
    }
    // Ensure src has not already been visited
    else {
        var i, len = _visited.length;
        for (i = 0; i < len; i++) {
            // If src was already visited, don't try to copy it, just return the reference
            if (src === _visited[i]) {
                return src;
            }
        }
    }

    // Add this object to the visited array
    _visited.push(src);

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

    //Special cases:
    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice(0) would soft clone
        ret = src.slice();
        var i = ret.length;
        while (i--){
            ret[i] = deepCopy(ret[i], _visited);
        }
        return ret;
    }
    //Date
    if (src instanceof Date) {
        return new Date(src.getTime());
    }
    //RegExp
    if (src instanceof RegExp) {
        return new RegExp(src);
    }
    //DOM Element
    if (src.nodeType && typeof src.cloneNode == 'function') {
        return src.cloneNode(true);
    }

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

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

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

0
npm install node-v8-clone

Najszybszy kloner, otwiera natywną metodę klonowania z node.js

var clone = require('node-v8-clone').clone;
var newObj = clone(obj, true); //true - deep recursive clone


0

Możesz także użyć tej biblioteki klonowania do głębokiego klonowania obiektów.

 npm install --save clone
const clone = require('clone');

const clonedObject = clone(sourceObject);

-2

Możesz prototypować obiekt, a następnie wywoływać instancję obiektu za każdym razem, gdy chcesz użyć i zmienić obiekt:

function object () {
  this.x = 5;
  this.y = 5;
}
var obj1 = new object();
var obj2 = new object();
obj2.x = 6;
console.log(obj1.x); //logs 5

Możesz także przekazać argumenty do konstruktora obiektów

function object (x, y) {
   this.x = x;
   this.y = y;
}
var obj1 = new object(5, 5);
var obj2 = new object(6, 6);
console.log(obj1.x); //logs 5
console.log(obj2.x); //logs 6

Mam nadzieję, że to jest pomocne.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.