การโคลนวัตถุใน Node.js


203

อะไรคือวิธีที่ดีที่สุดในการโคลนวัตถุใน node.js

เช่นฉันต้องการหลีกเลี่ยงสถานการณ์ที่:

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

วัตถุอาจมีประเภทที่ซับซ้อนเป็นคุณลักษณะดังนั้นการง่ายสำหรับ (var x ใน obj1) จะไม่สามารถแก้ไขได้ ฉันต้องเขียนโคลนซ้ำหรือไม่หรือมีบางอย่างในตัวที่ฉันไม่เห็น?


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

4
โปรดทราบว่าในความคิดเห็นของ @ SalmanPK ด้านบนนี่เป็นโคลนที่ตื้น ดังนั้นมันจึงใช้ได้กับตัวอย่างของ slifty แต่ถ้ามีอาร์เรย์หรือวัตถุที่ซ้อนกันพวกมันจะเป็นการอ้างอิง : /
Jesse

1
ฉันพบบทความนี้มีประโยชน์มาก: heyjavascript.com/4-creative-ways-to-clone-objects
Jordan Hudson

3
@Jordan Hudson - ใช้ JSON ได้ดีมากในตัวอย่างที่สอง var newObj = JSON.parse (JSON.stringify (oldObj)); // ตอนนี้ newObj เป็นโคลน ปัญหาเดียวคือ stringify จะไม่ทำงานกับการอ้างอิงแบบเรียกซ้ำดังนั้นจึงต้องระวัง
Kfir Erez

คำตอบ:


298

ความเป็นไปได้ 1

สำเนาลึกต่ำจีบ:

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

ความเป็นไปได้ 2 (คัดค้าน)

ข้อควรสนใจ: ขณะนี้โซลูชันนี้ถูกทำเครื่องหมายว่าเลิกใช้แล้วในเอกสารประกอบของ Node.js :

เมธอด util._extend () ไม่เคยถูกตั้งใจให้ใช้ภายนอกโมดูล Node.js ภายใน ชุมชนพบและใช้งานต่อไป

มันเลิกใช้แล้วและไม่ควรใช้ในรหัสใหม่ JavaScript มาพร้อมกับฟังก์ชันการทำงานในตัวที่คล้ายกันผ่าน Object.assign ()

คำตอบเดิม ::

สำหรับการคัดลอกตื้นให้ใช้util._extend()ฟังก์ชันในตัวของโหนด

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

ซอร์สโค้ดของ_extendฟังก์ชั่นของโหนดอยู่ที่นี่: 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
คำถามนี้เรียกว่าโคลนซ้ำโดยเฉพาะ นี่คือโคลนที่ตื้น
Benjamin Atkin

28
ชื่อไม่_*ควรจะแปลว่าเป็นวิธีการส่วนตัวและไม่ควรเชื่อถือใช่หรือไม่
ปุย

7
ทุกโปรเจ็กต์ JavaScript ที่มีขนาดใดก็ตามมีการใช้งานมากกว่าหนึ่งขยาย () และโหนดจะไม่มีข้อยกเว้น แกน Node.js ใช้ประโยชน์จากฟังก์ชั่นนี้ได้อย่างกว้างขวาง เพื่ออ้างอิงไอแซ็ก"มันจะไม่ไปไหนทุกเวลาเร็ว ๆ นี้"
jimbo

2
ทำงานอย่างสมบูรณ์แบบสำหรับฉัน ดีกว่ายุ่งกับ Object ต้นแบบ imo
Michael Dausmann

12
นี่คือคำตอบที่ผิด ตามเอกสารของโหนด: nodejs.org/api/util.html#util_util_extend_obj วิธีการก็ไม่เคยตั้งใจที่จะอยู่นอกมือสองของโมดูล Node.js ภายใน ชุมชนพบและใช้งานต่อไป มันเลิกใช้แล้วและไม่ควรใช้ในรหัสใหม่ JavaScript มาพร้อมกับฟังก์ชั่นในตัวที่คล้ายกันมากผ่านutil._extend() Object.assign().
Jordie

265

ฉันประหลาดใจที่Object.assignไม่ได้ถูกกล่าวถึง

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

หากมี (เช่น Babel) คุณสามารถใช้ตัวดำเนินการกระจายวัตถุ :

let cloned = { ... source };

1
คุณบันทึกวันของฉัน! ขอบคุณ
wzr1337

2
นี่เป็นวิธีที่ดีกว่าการนำเข้าห้องสมุดบุคคลที่สามหรือใช้วิธีการแก้ปัญหา JSON ที่ไม่สมบูรณ์ ขอบคุณ!
Neil S

75
นี่เป็นสำเนาตื้น ๆ
Jordan Davidson

14
คำเตือนสำหรับ Deep Clone, สำหรับ Deep โคลนยังคงต้องใช้ทางเลือกอื่น developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
......

1
จากสิ่งที่ฉันสามารถบอกได้ว่าโอเปอเรเตอร์การแพร่กระจายวัตถุไม่ใช่สิ่ง ES6 แต่เป็นข้อเสนอระยะที่ 3 ซึ่งหมายความว่าคุณสามารถใช้กับ Babel แต่ไม่ได้โดยไม่ต้องจากสิ่งที่ฉันเข้าใจ 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;
    }
});

นี่จะกำหนดวิธีการขยายที่คุณสามารถใช้ได้ รหัสมาจากบทความนี้


ฉันไม่เห็นว่ามันควรจะทำงานอย่างไร มันปรับเปลี่ยนวัตถุต้นฉบับ! ฉันควรจะใช้ฟังก์ชั่นนี้เพื่อให้ได้โคลนของวัตถุได้อย่างไร? คุณสามารถเพิ่มรหัสการใช้งานที่นี่ได้ไหม หลังจากอ่านโพสต์ของคุณและโพสต์บล็อกฉันยังคงไม่สามารถทราบได้ว่ามีวัตถุประสงค์เพื่อใช้ในการโคลนวัตถุอย่างไร
แบรด

3
มันใช้งานได้จริงเหรอ? "if (name in dest)" - จะเปลี่ยนคุณสมบัติเฉพาะเมื่อมีอยู่ในปลายทางเท่านั้น มันควรถูกทำให้ไร้ผล
memical

8
การดัดแปลง Object.prototype น่าจะเป็น verboten หรือไม่? นอกจากนี้ลิงค์บทความที่ขาด
Daniel Schaffer

เพิ่งลองลิงค์บทความและใช้งานได้สำหรับฉัน อาจเป็นเพราะเครือข่ายมีปัญหาเมื่อคุณลองใช้
Michael Dillon

จากความคิดเห็นจำนวนหนึ่งฉันได้อัปเดตคำตอบเพื่อรวมตัวแปรที่ไม่ได้เพิ่มลงในต้นแบบของวัตถุ
Shamasis Bhattacharya

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

2
สิ่งนี้ถูกแนะนำแล้วในคำตอบที่มีอยู่นี้ไม่มีจุดซ้ำ
Shadow Wizard เป็นหูสำหรับคุณ

@ShadowWizard เหล่านี้เป็นวิธีการที่แตกต่างกัน หนึ่งในนี้ก็จะแปลง JSON และกลับไปที่วัตถุในขณะที่มีการเชื่อมโยงการใช้งานคำตอบObject.keys()ย้ำถึงวัตถุ
Mente

คำตอบนี้ผิด JSON.stringify ใช้วัตถุวันที่และลดลงเป็นสตริงแล้วหลังจากแยกมันเป็นสตริงเพื่อที่จะกลายสภาพของวัตถุของคุณทำให้คุณมีวัตถุที่แตกต่างกว่าในกรณีของวันที่เริ่มต้น
twboc

13

คุณสามารถใช้ฟังก์ชั่นขยายจาก JQuery:

var newClone= jQuery.extend({}, oldObject);  
var deepClone = jQuery.extend(true, {}, oldObject); 

มีปลั๊กอินของ Node.js ด้วยเช่นกัน:

https://github.com/shimondoodkin/nodejs-clone-extend

ในการทำโดยไม่ใช้ JQuery หรือปลั๊กอินให้อ่านที่นี่:

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


ปลั๊กอิน Node.js สมบูรณ์แบบ ขอบคุณ!
Justin

คำตอบที่ดี ฉันชอบโมดูลที่มีประโยชน์เล็กน้อย
Chev


8

มีโมดูล Node บางส่วนอยู่หากไม่ต้องการ "ย้อนกลับไปเอง" อันนี้ดูดี: https://www.npmjs.com/package/clone

ดูเหมือนว่ามันจะจัดการกับทุกสิ่งรวมถึงการอ้างอิงแบบวงกลม จากหน้าGitHub :

โคลนต้นแบบโคลนวัตถุอาร์เรย์วันที่วัตถุและวัตถุ RegEx ทุกอย่างถูกโคลนซ้ำเพื่อให้คุณสามารถโคลนวันที่ในอาร์เรย์ในวัตถุเช่น [... ] การอ้างอิงแบบวงกลม? อ้อ!


7

รหัสนี้ยังใช้งานได้สาเหตุวิธีการObject.create ()สร้างวัตถุใหม่ที่มีวัตถุต้นแบบและคุณสมบัติที่ระบุ

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

var obj2 = Object.create(obj1);

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

obj1.x; //5

4
นี่เป็นสำเนาตื้น ๆ
Radagast the Brown

6

วิธีที่ง่ายและเร็วที่สุดในการโคลนวัตถุใน NodeJS คือการใช้วิธีการ 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"}

วิธีการ Object.keys ต้องใช้ JavaScript 1.8.5; nodeJS v0.4.11 รองรับวิธีนี้

แต่แน่นอนสำหรับวัตถุที่ซ้อนกันจำเป็นต้องใช้ func แบบเรียกซ้ำ


โซลูชันอื่น ๆ คือการใช้ JSON ดั้งเดิม (ใช้งานใน JavaScript 1.7) แต่มันช้ากว่ามาก (ช้ากว่า ~ 10 เท่า) จากก่อนหน้านี้

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

5

นอกจากนี้ยังมีโครงการใน Github ที่มุ่งหวังที่จะเป็นท่าเรือโดยตรงของjQuery.extend():

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

ตัวอย่างเช่นแก้ไขจากเอกสาร 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

คุณทุกคนทุกข์ทรมาน แต่การแก้ปัญหานั้นง่าย

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

var obj2 = {...obj1}; // บูม


3

กำลังมองหาตัวเลือกการโคลนที่แท้จริงฉันสะดุดข้ามลิงก์ของ ridcully ที่นี่:

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

ฉันแก้ไขวิธีแก้ไขปัญหาในหน้านั้นเพื่อให้ฟังก์ชั่นที่แนบมากับObjectต้นแบบไม่สามารถนับได้ นี่คือผลลัพธ์ของฉัน:

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

หวังว่านี่จะช่วยคนอื่นเช่นกัน โปรดทราบว่ามีบางประการ ... โดยเฉพาะอย่างยิ่งกับคุณสมบัติที่ชื่อว่า "โคลน" มันใช้งานได้ดีสำหรับฉัน ฉันไม่ได้รับเครดิตสำหรับการเขียน อีกครั้งฉันเปลี่ยนวิธีการกำหนด


นี่เป็นสิ่งที่ผิด ประเภทวันที่เป็นวัตถุดังนั้นรหัสนี้จะแทนที่วันที่ด้วยวัตถุว่างเปล่า ... อย่าใช้สิ่งนี้
jtblin


0

หากคุณใช้งานสคริปต์กาแฟมันง่ายเหมือน:

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

แม้ว่านี่ไม่ใช่การโคลนนิ่งที่ลึก


0

ไม่มีคำตอบใดที่ทำให้ฉันพอใจหลาย ๆ คนไม่ทำงานหรือเป็นเพียงโคลนตื้นคำตอบจาก @ clint-harris และการใช้ JSON.parse / stringify ค่อนข้างดี แต่ค่อนข้างช้า ฉันพบโมดูลที่ทำการโคลนนิ่งอย่างรวดเร็ว: https://github.com/AlexeyKupershtokh/node-v8-clone


0

ไม่มีวิธีในตัวที่จะทำการโคลนจริง (คัดลอกลึก) ของวัตถุใน node.js มีบางกรณีที่มีขอบหากินดังนั้นคุณควรใช้ห้องสมุดนี้ ฉันเขียนฟังก์ชั่นดังกล่าวสำหรับห้องสมุดsimpleooของฉัน คุณสามารถใช้deepCopyฟังก์ชั่นโดยไม่ต้องใช้สิ่งอื่นจากไลบรารี (ซึ่งค่อนข้างเล็ก) หากคุณไม่ต้องการ ฟังก์ชันนี้รองรับการโคลนหลายประเภทข้อมูลรวมถึงอาร์เรย์วันที่และนิพจน์ทั่วไปรองรับการอ้างอิงแบบเรียกซ้ำและยังทำงานกับวัตถุที่ฟังก์ชันตัวสร้างมีพารามิเตอร์ที่ต้องการ

นี่คือรหัส:

//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

cloner ที่เร็วที่สุดจะเปิดวิธีเนทิฟโคลนจาก node.js

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

0

อีกวิธีหนึ่งคือการแค็ปซูลโดยตรงในตัวแปรใหม่โดยใช้: obj1= {...obj2}


นี่เป็นสำเนาที่ตื้น
Rémi Doolaeghe

0

คุณยังสามารถใช้ไลบรารีโคลนนี้เพื่อวัตถุโคลนนิ่งลึก

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

const clonedObject = clone(sourceObject);

-2

คุณสามารถสร้างวัตถุต้นแบบแล้วเรียกวัตถุเช่นทุกครั้งที่คุณต้องการใช้และเปลี่ยนวัตถุ:

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

คุณยังสามารถส่งผ่านข้อโต้แย้งไปยังตัวสร้างวัตถุ

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

หวังว่านี่จะเป็นประโยชน์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.