Nodejs: cara mengkloning objek

91

Jika saya mengkloning sebuah array, saya menggunakan cloneArr = arr.slice()

Saya ingin tahu cara mengkloning objek di nodejs.

guilin 桂林
sumber

Jawaban:

178

Untuk utilitas dan kelas di mana tidak perlu memeras setiap penurunan kinerja, saya sering menipu dan hanya menggunakan JSON untuk melakukan salinan dalam:

function clone(a) {
   return JSON.parse(JSON.stringify(a));
}

Ini bukan satu-satunya jawaban atau jawaban yang paling elegan; semua jawaban lain harus dipertimbangkan untuk mengatasi kemacetan produksi. Namun, ini adalah solusi cepat dan kotor, cukup efektif, dan berguna dalam banyak situasi di mana saya akan mengkloning hash properti sederhana.

Kato
sumber
2
@djechlin Tentu saja. Cobalah: jsfiddle.net/katowulf/E5jC3 (diuji dengan node 0.10.11) Ini tidak akan dapat menyusun kembali fungsi atau data prototipe, tetapi nilai-nilainya baik-baik saja.
Kato
13
Ini akan mengubah tanggal menjadi string
backus
4
@Backus Serta objek dan fungsi.
Kato
2
@Kato, Mengapa referensi melingkar menyiratkan bahwa tidak perlu membuat salinan dalam? Mereka adalah dua hal yang tidak berhubungan. Sangat mungkin untuk menulis metode penggandaan yang baik untuk NodeJS yang mendukung referensi melingkar dan tidak menggunakan JSON untuk mengkloning. github.com/pvorb/node-clone
Samuel Neff
2
Sudah dicatat di komentar dan di deskripsi. Anda tidak dapat mengkloning fungsi, prototipe objek, atau hal lain yang tidak boleh Anda coba lakukan dengan metode klon umum. Anda akan membutuhkan lebih dari satu baris kode untuk itu, dan mungkin tidak boleh mengkloning sampah itu - letakkan metode klon di kelas Anda yang tahu bagaimana menangani internal dan melakukannyanewObj = obj.clone(args...);
Kato
35

Object.assign belum disebutkan dalam salah satu jawaban di atas.

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

Jika Anda menggunakan ES6, Anda dapat menggunakan operator penyebaran:

let cloned = { ... source };

Referensi: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Sam G
sumber
29
Perhatikan bahwa keduanya adalah klon dangkal dan operator penyebaran pada objek membutuhkan node.js 8+.
jlh
1
Seperti yang disebutkan @jlh, ini tidak akan berfungsi untuk objek bersarang. Anda dapat membaca lebih lanjut tentang masalah klon mendalam dari metode ini di sini: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Shnd
Seperti yang dikatakan Shnd, jawaban ini salah. Untuk kloning mendalam, kita perlu menggunakan alternatif lain karena Object.assign () menyalin nilai properti. Jika nilai sumber adalah referensi ke suatu objek, itu hanya menyalin nilai referensi itu.
troy
1
Ini tidak mengkloning, ini hanya mereferensikan objek aslinya, artinya jika Anda mengubah sesuatu, itu akan masuk ke master
Aidan Welch
@AidanWelch Saya rasa itu tidak benar. let a = {x:1}; let b = Object.assign({}, a); a.x = 2; a.y = 1; console.log(a, b);
Miguel Pynto
20

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

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

master klon mengkloning objek, array, objek Tanggal, dan objek RegEx. Semuanya dikloning secara rekursif, sehingga Anda bisa mengkloning tanggal dalam array di objek, misalnya. [...] Referensi melingkar? Ya!

Clint Harris
sumber
10

Sulit untuk melakukan operasi kloning yang umum tetapi berguna karena apa yang harus dikloning secara rekursif dan apa yang harus disalin tergantung pada bagaimana objek tertentu seharusnya bekerja.

Sesuatu yang mungkin berguna adalah

function clone(x)
{
    if (x === null || x === undefined)
        return x;
    if (typeof x.clone === "function")
        return x.clone();
    if (x.constructor == Array)
    {
        var r = [];
        for (var i=0,n=x.length; i<n; i++)
            r.push(clone(x[i]));
        return r;
    }
    return x;
}

Dalam kode ini logikanya adalah

  • dalam kasus nullatau undefinedhanya mengembalikan yang sama (kasus khusus diperlukan karena ini adalah kesalahan untuk mencoba melihat apakah ada clonemetode)
  • apakah benda tersebut memiliki a clone metode? lalu gunakan itu
  • apakah objeknya sebuah array? kemudian lakukan operasi kloning rekursif
  • jika tidak, kembalikan nilai yang sama

Fungsi klon ini memungkinkan penerapan metode klon khusus dengan mudah ... misalnya

function Point(x, y)
{
    this.x = x;
    this.y = y;

    ...
}

Point.prototype.clone = function()
{
    return new Point(this.x, this.y);
};



function Polygon(points, style)
{
    this.points = points;
    this.style = style;

    ...
}

Polygon.prototype.clone = function()
{
    return new Polygon(clone(this.points),
                       this.style);
};

Ketika di objek Anda tahu bahwa operasi kloning yang benar untuk larik tertentu hanyalah salinan dangkal maka Anda dapat memanggil values.slice() bukan clone(values).

Misalnya dalam kode di atas, saya secara eksplisit mengharuskan kloning objek poligon akan mengkloning poin, tetapi akan menggunakan objek gaya yang sama. Jika saya ingin mengkloning objek gaya juga, maka saya bisa langsung saja clone(this.style).

6502
sumber
1
1 untuk menyebutkan bahwa objek perlu menerapkan .clonemetode itu sendiri. Ini adalah cara terbaik untuk menangani objek kloning.
Raynos
3
if (x.clone)harusif (typeof x.clone === 'function')
Yanick Rochon
@YanickRochon: Terima kasih, sudah diperbaiki. Maaf, entah mengapa saya tidak memperhatikan komentar ini sebelumnya ...
6502
9

Tidak ada metode asli untuk mengkloning objek. Mengimplementasikan _.clonegaris bawah yang merupakan klon dangkal.

_.clone = function(obj) {
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

Itu baik mengirisnya atau memanjangkannya.

Ini dia _.extend

// extend the obj (first parameter)
_.extend = function(obj) {
  // for each other parameter
  each(slice.call(arguments, 1), function(source) {
    // loop through all properties of the other objects
    for (var prop in source) {
      // if the property is not undefined then add it to the object.
      if (source[prop] !== void 0) obj[prop] = source[prop];
    }
  });
  // return the object (first parameter)
  return obj;
};

Perluas hanya dengan mengulang semua item dan membuat objek baru dengan item di dalamnya.

Anda dapat meluncurkan penerapan naif Anda sendiri jika mau

function clone(o) {
  var ret = {};
  Object.keys(o).forEach(function (val) {
    ret[val] = o[val];
  });
  return ret;
}

Ada alasan bagus untuk menghindari kloning yang dalam karena closure tidak bisa dikloning.

Saya pribadi telah mengajukan pertanyaan tentang deep cloning objects beforedan kesimpulan yang saya dapatkan adalah Anda tidak melakukannya.

Rekomendasi saya adalah gunakan underscoredan _.clonemetode itu untuk klon dangkal

Raynos
sumber
9

Untuk salinan yang dangkal, saya suka menggunakan pola pengurangan (biasanya dalam modul atau semacamnya), seperti:

var newObject = Object.keys(original).reduce(function (obj, item) {
    obj[item] = original[item];
    return obj;
},{});

Berikut adalah jsperf untuk beberapa opsi: http://jsperf.com/shallow-copying

Olli K
sumber
sederhana, elegan, cemerlang. Saya menantikan kontribusi SO Anda yang lain ^ _ ^
Terima kasih
7

Pertanyaan lama, tetapi ada jawaban yang lebih elegan dari yang telah disarankan sejauh ini; gunakan utils._extend bawaan:

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

var varToCopy = { test: 12345, nested: { val: 6789 } };

var copiedObject = extend({}, varToCopy);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 } }

Perhatikan penggunaan parameter pertama dengan objek kosong {} - ini memberitahu perluasan bahwa objek yang disalin perlu disalin ke objek baru. Jika Anda menggunakan objek yang ada sebagai parameter pertama, maka parameter kedua (dan semua parameter berikutnya) akan disalin-penggabungan-dalam di atas variabel parameter pertama.

Menggunakan variabel contoh di atas, Anda juga dapat melakukan ini:

var anotherMergeVar = { foo: "bar" };

extend(copiedObject, { anotherParam: 'value' }, anotherMergeVar);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 }, anotherParam: 'value', foo: 'bar' }

Utilitas yang sangat berguna, terutama di mana saya biasa memperluas AngularJS dan jQuery.

Semoga ini bisa membantu orang lain; penimpaan referensi objek adalah kesengsaraan, dan ini menyelesaikannya setiap saat!

Scott Byers
sumber
1
Penggunaan _extend sekarang disusutkan. Gunakan Object.assign () sebagai gantinya: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
MacroMan
7

Anda juga bisa menggunakan lodash . Ini memiliki metode clone dan cloneDeep .

var _= require('lodash');

var objects = [{ 'a': 1 }, { 'b': 2 }];

var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
Michal
sumber
1

Bergantung pada apa yang ingin Anda lakukan dengan objek kloning Anda, Anda dapat memanfaatkan mekanisme pewarisan prototipe javascript dan mencapai objek yang dikloning melalui:

var clonedObject = Object.create(originalObject);

Ingatlah bahwa ini bukan klon lengkap - baik atau buruk.

Hal yang baik tentang itu adalah Anda sebenarnya belum menduplikasi objek sehingga jejak memori akan rendah.

Beberapa hal rumit yang perlu diingat tentang metode ini adalah bahwa iterasi properti yang ditentukan dalam rantai prototipe terkadang bekerja sedikit berbeda dan fakta bahwa setiap perubahan pada objek asli akan memengaruhi objek kloning juga kecuali properti itu telah disetel pada dirinya sendiri juga. .

VoxPelli
sumber
3
Ini sangat konyol dan berbahaya dan sama sekali tidak seperti tiruan. var a = {foo: "bar"}, b = Object.create(a); a.foo = "broken"; console.log(b.foo); // "broken";
Terima kasih
@naomik: b.foo = "working"; console.log(a.foo); // still "broken";Orang tentu harus menyadari bahwa perubahan pada objek asli akan tercermin dalam "klon" dan bahwa perubahan dalam "klon" tidak akan tercermin dalam objek asli - tetapi saya tidak akan menyebutnya berbahaya - itu semua tergantung pada apa yang ingin Anda lakukan dengan klon Anda
VoxPelli
@naomik: Saya cukup baru di JS, jadi saya ingin beberapa presisi. Klon dangkal yang diusulkan oleh olli-k dan yang Anda sukai tampaknya (IMHO, tapi saya mungkin salah) memiliki batasan yang sama daripada solusi yang diusulkan oleh voxpelli. Penutup asli dan dangkal juga akan berbagi "item" yang sama, memperbarui item dalam aslinya juga akan memengaruhi klon dangkal. Atau?
bmorin
@bmornin, jika Anda menggunakan teknik Olli K, memodifikasi objek asli tidak akan menyebabkan perubahan pada objek baru.
Terima kasih
1
@VoxPelli Seluruh tujuan dari "klon" adalah untuk mengisolasi perubahan dari objek aslinya, oleh karena itu metode ini lebih berbahaya.
Doug
1

Saya menerapkan salinan dalam lengkap. Saya percaya itu adalah pilihan terbaik untuk metode klon generik, tetapi tidak menangani referensi siklis.

Contoh penggunaan:

parent = {'prop_chain':3}
obj = Object.create(parent)
obj.a=0; obj.b=1; obj.c=2;

obj2 = copy(obj)

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3
console.log(obj2, obj2.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3

parent.prop_chain=4
obj2.a = 15

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 4
console.log(obj2, obj2.prop_chain)
// '{'a':15, 'b':1, 'c':2} 4

Kode itu sendiri:

Kode ini menyalin objek dengan prototipe mereka, itu juga menyalin fungsi (mungkin berguna untuk seseorang).

function copy(obj) {
  // (F.prototype will hold the object prototype chain)
  function F() {}
  var newObj;

  if(typeof obj.clone === 'function')
    return obj.clone()

  // To copy something that is not an object, just return it:
  if(typeof obj !== 'object' && typeof obj !== 'function' || obj == null)
    return obj;

  if(typeof obj === 'object') {    
    // Copy the prototype:
    newObj = {}
    var proto = Object.getPrototypeOf(obj)
    Object.setPrototypeOf(newObj, proto)
  } else {
    // If the object is a function the function evaluate it:
    var aux
    newObj = eval('aux='+obj.toString())
    // And copy the prototype:
    newObj.prototype = obj.prototype
  }

  // Copy the object normal properties with a deep copy:
  for(var i in obj) {
    if(obj.hasOwnProperty(i)) {
      if(typeof obj[i] !== 'object')
        newObj[i] = obj[i]
      else
        newObj[i] = copy(obj[i])
    }
  }

  return newObj;
}

Dengan salinan ini saya tidak dapat menemukan perbedaan antara yang asli dan yang disalin kecuali jika yang asli menggunakan penutupan pada konstruksinya, jadi saya pikir ini adalah implementasi yang baik.

Saya harap ini membantu

VinGarcia
sumber
1

untuk array, seseorang dapat menggunakan

var arr = [1,2,3]; 
var arr_2 = arr ; 

print ( arr_2 ); 

arr = arr.slice (0);

print ( arr ); 

arr[1]=9999; 

print ( arr_2 ); 
tlqtangok.dll
sumber
1

Objek dan Array dalam JavaScript menggunakan panggilan dengan referensi, jika Anda memperbarui nilai yang disalin itu mungkin mencerminkan objek aslinya. Untuk mencegah hal ini, Anda dapat mengkloning objek secara mendalam, untuk mencegah referensi diteruskan, menggunakan perintah run method lodash library cloneDeep

npm instal lodash

const ld = require('lodash')
const objectToCopy = {name: "john", age: 24}
const clonedObject = ld.cloneDeep(objectToCopy)
vinay dagar
sumber