Mengkloning Objek di Node.js

203

Apa cara terbaik untuk mengkloning suatu objek di node.js

misalnya saya ingin menghindari situasi di mana:

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

Objek mungkin mengandung tipe kompleks sebagai atribut, jadi sederhana untuk (var x in obj1) tidak akan menyelesaikannya. Apakah saya perlu menulis klon rekursif sendiri atau ada sesuatu yang dibangun yang tidak saya lihat?

licin
sumber
23
1. npm install underscore2. var _ = require('underscore')3 _.clone(objToClone).;
Salman von Abbas
4
Perhatikan bahwa dalam komentar @ SalmanPK di atas, ini adalah klon yang dangkal . jadi itu akan berfungsi untuk contoh slifty, tetapi jika ada array atau objek bersarang, mereka akan menjadi referensi. : /
Jesse
1
Saya menemukan artikel ini sangat membantu: heyjavascript.com/4-creative-ways-to-clone-objects
Jordan Hudson
3
@ Jordan Hudson - Penggunaan JSON yang sangat bagus dalam contoh kedua. var newObj = JSON.parse (JSON.stringify (oldObj)); // Sekarang newObj adalah tiruan. Satu-satunya masalah adalah bahwa strify tidak akan bekerja pada referensi rekursif jadi perlu berhati-hati.
Kfir Erez

Jawaban:

298

Kemungkinan 1

Salinan dalam tanpa embel-embel:

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

Kemungkinan 2 (usang)

Perhatian: Solusi ini sekarang ditandai sebagai usang dalam dokumentasi Node.js :

Metode util._extend () tidak pernah dimaksudkan untuk digunakan di luar modul Node.js internal. Komunitas tetap menemukan dan menggunakannya.

Itu sudah usang dan tidak boleh digunakan dalam kode baru. JavaScript hadir dengan fungsionalitas bawaan yang sangat mirip melalui Object.assign ().

Jawaban asli ::

Untuk salinan yang dangkal, gunakan util._extend()fungsi bawaan 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

Kode sumber _extendfungsi Node ada di sini: 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;
};
jimbo
sumber
5
Pertanyaan itu secara spesifik meminta kloning rekursif. Ini adalah tiruan yang dangkal.
Benjamin Atkin
28
Bukankah nama seperti _*seharusnya berarti itu adalah metode pribadi dan tidak boleh diandalkan?
Fluffy
7
Setiap proyek JavaScript dari berbagai ukuran memiliki satu atau lebih implementasi dari extended (), dan Node tidak terkecuali. Inti Node.js memanfaatkan fungsi ini secara ekstensif. Mengutip Isaacs, "Ini tidak akan kemana-mana dalam waktu dekat."
jimbo
2
bekerja dengan baik untuk saya. jauh lebih baik daripada bermain-main dengan prototipe Object imo
Michael Dausmann
12
Ini jawaban yang SALAH. Sesuai dokumentasi node: nodejs.org/api/util.html#util_util_extend_obj The util._extend()Metode tidak pernah dimaksudkan untuk menjadi digunakan di luar modul Node.js internal. Komunitas tetap menemukan dan menggunakannya. Itu sudah usang dan tidak boleh digunakan dalam kode baru. JavaScript hadir dengan fungsionalitasObject.assign().
bawaan yang
265

Saya terkejut Object.assignbelum disebutkan.

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

Jika tersedia (misalnya Babel), Anda dapat menggunakan operator penyebaran objek :

let cloned = { ... source };
djanowski
sumber
1
kamu menyelamatkan hariku! Terima kasih
wzr1337
2
ini adalah solusi yang jauh lebih baik daripada harus mengimpor perpustakaan pihak ketiga atau menggunakan solusi JSON yang tidak sempurna. Terima kasih!
Neil S
75
ini adalah salinan yang dangkal
Jordan Davidson
14
Peringatan untuk Deep Clone, Untuk deep clone masih perlu menggunakan alternatif lain. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
gsalgadotoledo
1
Dari apa yang saya tahu operator objek menyebar bukanlah hal ES6 melainkan proposal tahap 3. yang berarti Anda dapat menggunakannya dengan babel tetapi bukan tanpa dari apa yang saya mengerti. 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;
    }
});

Ini akan menentukan metode perluasan yang dapat Anda gunakan. Kode berasal dari artikel ini.

Michael Dillon
sumber
Saya tidak melihat bagaimana ini seharusnya bekerja. Itu memodifikasi Objek asli! Bagaimana saya bisa menggunakan fungsi ini untuk mendapatkan tiruan dari suatu objek? Bisakah Anda menambahkan beberapa kode penggunaan di sini? Setelah membaca posting Anda dan posting blog, saya masih tidak tahu bagaimana ini dimaksudkan untuk digunakan untuk mengkloning suatu objek.
Brad
3
apakah ini benar-benar berfungsi? "if (name in dest)" - hanya akan mengubah properti jika sudah ada di dest. itu harus dinegasikan.
memical
8
Bukankah memodifikasi Object.prototype seharusnya menjadi verboten? Juga tautan artikel itu rusak.
Daniel Schaffer
Baru saja mencoba tautan artikel dan itu berhasil untuk saya. Mungkin itu blip jaringan ketika Anda mencobanya.
Michael Dillon
Berdasarkan sejumlah komentar, saya telah memperbarui jawaban untuk menyertakan varian yang tidak menambah prototipe objek.
Shamasis Bhattacharya
20
var obj2 = JSON.parse(JSON.stringify(obj1));
pengguna2516109
sumber
2
Ini sudah disarankan di jawaban yang ada ini tidak ada gunanya mengulanginya.
Shadow Wizard adalah Ear For You
@ShadowWizard ini adalah metode yang berbeda. Ini hanya mengkonversi ke json dan kembali ke objek, sedangkan jawaban terkait digunakan Object.keys()untuk beralih melalui objek
mente
Jawaban ini salah. JSON.stringify mengambil objek tanggal dan menguranginya menjadi string, dan kemudian setelah menguraikannya meninggalkannya sebagai string sehingga mengubah kondisi objek Anda meninggalkan Anda dengan objek yang berbeda dari awalnya dalam hal tanggal.
twboc
8

Ada beberapa modul Node di luar sana jika tidak ingin "melempar sendiri". Yang ini terlihat bagus: https://www.npmjs.com/package/clone

Sepertinya itu menangani semua jenis barang, termasuk referensi melingkar. Dari halaman github :

master kloning objek kloning, array, objek Date, dan objek RegEx. Semuanya dikloning secara rekursif, sehingga Anda dapat mengkloning tanggal dalam array dalam objek, misalnya. [...] Referensi melingkar? Ya!

Clint Harris
sumber
7

Kode ini juga berfungsi karena metode Object.create () membuat objek baru dengan objek dan properti prototipe yang ditentukan.

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

var obj2 = Object.create(obj1);

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

obj1.x; //5
Hiron
sumber
4
ini adalah salinan dangkal
Radagast the Brown
6

Sederhana dan cara tercepat untuk mengkloning Objek di NodeJS adalah dengan menggunakan metode 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"}

Metode Object.keys membutuhkan JavaScript 1.8.5; nodeJS v0.4.11 mendukung metode ini

tapi tentu saja untuk objek bersarang perlu menerapkan fungsi rekursif


Solusi lain adalah dengan menggunakan JSON asli (Diimplementasikan dalam JavaScript 1.7), tetapi jauh lebih lambat (~ 10 kali lebih lambat) daripada yang sebelumnya

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

Ada juga proyek di Github yang bertujuan untuk menjadi pelabuhan yang lebih langsung jQuery.extend() :

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

Contoh, dimodifikasi dari dokumen 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);
Randy
sumber
4

Kalian semua menderita namun solusinya sederhana.

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

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

Ngend Lio
sumber
3

Mencari opsi tiruan sejati, saya menemukan tautan ridcully di sini:

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

Saya memodifikasi solusi pada halaman tersebut sehingga fungsi yang dilampirkan pada Objectprototipe tidak dapat dihitung. Inilah hasil saya:

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

Semoga ini bisa membantu orang lain juga. Perhatikan bahwa ada beberapa peringatan ... terutama dengan properti bernama "clone". Ini bekerja dengan baik untuk saya. Saya tidak mengambil kredit apa pun untuk menulisnya. Sekali lagi, saya hanya mengubah cara mendefinisikannya.

Brad
sumber
Ini salah. Jenis tanggal adalah objek sehingga kode ini akan mengganti tanggal dengan objek kosong ... Jangan gunakan ini.
jtblin
0

Jika Anda menggunakan skrip kopi, semudah:

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

Padahal ini bukan tiruan yang dalam.

balupton
sumber
0

Tidak ada jawaban yang memuaskan saya, beberapa tidak berfungsi atau hanya klon yang dangkal, jawaban dari @ clint-harris dan menggunakan JSON.parse / stringify bagus tapi cukup lambat. Saya menemukan modul yang melakukan kloning cepat dalam: https://github.com/AlexeyKupershtokh/node-v8-clone

jtblin
sumber
0

Tidak ada built-in cara untuk melakukan klon nyata (salinan dalam) dari suatu objek di node.js. Ada beberapa kasus tepi yang rumit sehingga Anda harus menggunakan perpustakaan untuk ini. Saya menulis fungsi seperti itu untuk perpustakaan simpleoo saya . Anda dapat menggunakandeepCopy fungsi tanpa menggunakan apa pun dari perpustakaan (yang cukup kecil) jika Anda tidak membutuhkannya. Fungsi ini mendukung kloning beberapa tipe data, termasuk array, tanggal, dan ekspresi reguler, mendukung referensi rekursif, dan juga bekerja dengan objek yang fungsi konstruktornya memiliki parameter yang diperlukan.

Ini kodenya:

//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;
};
Matt Browne
sumber
0
npm install node-v8-clone

Cloner tercepat, itu membuka metode klon asli dari node.js

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

Solusi lain adalah merangkum langsung dalam variabel baru menggunakan: obj1= {...obj2}

labelle
sumber
Ini adalah salinan dangkal
Rémi Doolaeghe
0

Anda juga dapat menggunakan pustaka klon ini untuk objek klon dalam.

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

const clonedObject = clone(sourceObject);
Lanil Marasinghe
sumber
-2

Anda bisa membuat prototipe objek dan kemudian memanggil objek contoh setiap kali Anda ingin menggunakan dan mengubah objek:

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

Anda juga bisa meneruskan argumen ke konstruktor objek

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

Semoga ini bisa membantu.

pengguna3459287
sumber