Nhân bản một đối tượng trong Node.js


203

Cách tốt nhất để sao chép một đối tượng trong node.js là gì

ví dụ tôi muốn tránh tình huống:

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

Đối tượng cũng có thể chứa các loại phức tạp như các thuộc tính, vì vậy một cách đơn giản cho (var x in obj1) sẽ không giải quyết được. Tôi có cần phải tự viết một bản sao đệ quy hay có thứ gì đó được xây dựng trong đó mà tôi không nhìn thấy không?


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

4
Lưu ý rằng trong nhận xét của @ SalmanPK ở trên, đây là một bản sao nông . vì vậy nó sẽ hoạt động với ví dụ của slifty, nhưng nếu có các mảng hoặc đối tượng lồng nhau, chúng sẽ là các tham chiếu. : /
Jesse

1
Tôi thấy bài viết này rất hữu ích: heyjavascript.com/4-creative-ways-to-clone-objects
Jordan Hudson

3
@Jordan Hudson - Việc sử dụng JSON rất hay trong ví dụ thứ hai. var newObj = JSON.parse (JSON.opesify (oldObj)); // Bây giờ newObj là một bản sao. Vấn đề duy nhất là Stringify sẽ không hoạt động trên tham chiếu đệ quy nên cần phải cẩn thận.
Kfir Erez

Câu trả lời:


298

Khả năng 1

Bản sao sâu thấp frills:

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

Khả năng 2 (không dùng nữa)

Chú ý: Giải pháp này hiện được đánh dấu là không dùng nữa trong tài liệu của Node.js :

Phương thức produc._extend () không bao giờ được sử dụng ngoài các mô-đun Node.js nội bộ. Cộng đồng tìm thấy và sử dụng nó anyway.

Nó không được dùng nữa và không nên được sử dụng trong mã mới. JavaScript đi kèm với chức năng tích hợp rất giống nhau thông qua Object.assign ().

Câu trả lời gốc ::

Đối với một bản sao nông, sử dụng util._extend()chức năng tích hợp của 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

Mã nguồn của _extendchức năng của Node có tại đây: 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
Câu hỏi đặc biệt kêu gọi một bản sao đệ quy. Đây là một bản sao nông.
Benjamin Atkin

28
Không phải là một cái tên như _*được cho là có nghĩa là một phương thức riêng tư và không nên dựa vào?
Fluffy

7
Mỗi dự án JavaScript có kích thước bất kỳ đều có một hoặc nhiều triển khai của ext () và Node cũng không ngoại lệ. Lõi Node.js sử dụng rộng rãi chức năng này. Trích lời Isaacs, "Nó sẽ không đi đâu cả sớm đâu."
jimbo

2
làm việc hoàn hảo cho tôi tốt hơn nhiều so với việc gây rối với imo nguyên mẫu Object
Michael Dausmann

12
Đây là câu trả lời SAU. Theo tài liệu của nút: nodejs.org/api/util.html#util_util_extend_obj Các util._extend()phương pháp không bao giờ được dự định được sử dụng bên ngoài các module Node.js nội bộ. Cộng đồng tìm thấy và sử dụng nó anyway. Nó không được dùng nữa và không nên được sử dụng trong mã mới. JavaScript đi kèm với chức năng tích hợp rất giống nhau thông quaObject.assign().
Jordie

264

Tôi ngạc nhiên Object.assignđã không được đề cập.

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

Nếu có sẵn (ví dụ Babel), bạn có thể sử dụng toán tử trải rộng đối tượng :

let cloned = { ... source };

1
bạn đã cứu ngày của tôi! Cảm ơn
wzr1337

2
đây là một giải pháp tốt hơn nhiều so với việc phải nhập thư viện của bên thứ ba hoặc sử dụng cách giải quyết JSON không hoàn hảo. Cảm ơn!
Neil S

75
đây là một bản sao nông
Jordan Davidson

14
Cảnh báo cho Deep Clone, Đối với bản sao sâu vẫn cần sử dụng các lựa chọn thay thế khác. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
triệt

1
Từ những gì tôi có thể nói toán tử trải rộng đối tượng không phải là một điều ES6 mà thay vào đó là một đề xuất giai đoạn 3. có nghĩa là bạn có thể sử dụng nó với babel nhưng không phải từ những gì tôi hiểu. github.com/tc39/ từ
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;
    }
});

Điều này sẽ xác định một phương thức mở rộng mà bạn có thể sử dụng. Mã đến từ bài viết này.


Tôi không thấy cách này được cho là hoạt động. Nó sửa đổi đối tượng ban đầu! Làm thế nào tôi có thể sử dụng chức năng này để có được một bản sao của một đối tượng? Bạn có thể thêm một số mã sử dụng ở đây? Sau khi đọc bài đăng của bạn và bài đăng trên blog, tôi vẫn không thể hiểu làm thế nào nó được sử dụng để nhân bản một đối tượng.
Brad

3
cái này có thực sự hiệu quả không? "if (name in Dest)" - sẽ chỉ thay đổi thuộc tính nếu nó đã tồn tại trong mệnh. nó nên được phủ nhận
đáng nhớ

8
Không sửa đổi Object.prototype được cho là verboten? Ngoài ra liên kết bài viết bị hỏng.
Daniel Schaffer

Chỉ cần thử liên kết bài viết và nó hoạt động cho tôi. Có lẽ đó là một đốm sáng mạng khi bạn thử nó.
Michael Dillon

Dựa trên một số ý kiến, tôi đã cập nhật câu trả lời để bao gồm một biến thể không thêm vào nguyên mẫu của đối tượng.
Shamocation Bhattacharya

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

2
Điều này đã được đề xuất trong câu trả lời hiện tại không có điểm nào lặp lại nó.
Shadow Wizard là Ear For You

@ShadowWizard đây là những phương pháp khác nhau. Điều này chỉ đơn giản là chuyển đổi thành json và trở lại đối tượng, trong khi câu trả lời được liên kết sử dụng Object.keys()để lặp qua đối tượng
mente

Câu trả lời này là sai. JSON.opesify lấy một đối tượng ngày và thu nhỏ nó thành một chuỗi, và sau đó sau khi phân tích cú pháp, nó sẽ biến nó thành một chuỗi để nó biến đổi trạng thái của đối tượng để lại cho bạn một đối tượng khác so với ban đầu trong trường hợp ngày.
twboc

13

Bạn có thể sử dụng hàm mở rộng từ JQuery:

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

Cũng có một Plugin Node.js:

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

Để làm điều đó mà không cần JQuery hoặc Plugin, hãy đọc nó ở đây:

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


Plugin Node.js là hoàn hảo. Cảm ơn!
Justin

Câu trả lời chính xác. Tôi yêu các mô-đun nhỏ hữu ích.
Chev


8

Có một số mô-đun Node ngoài kia nếu không muốn "tự cuộn". Cái này có vẻ tốt: https://www.npmjs.com/package/clone

Có vẻ như nó xử lý tất cả các loại công cụ, bao gồm cả tài liệu tham khảo tròn. Từ trang github :

chủ nhân bản sao nhân bản các đối tượng, mảng, đối tượng ngày và các đối tượng RegEx. Tất cả mọi thứ được nhân bản đệ quy, để bạn có thể sao chép ngày trong mảng trong các đối tượng, ví dụ. [...] Tài liệu tham khảo thông tư? Vâng!


7

Mã này cũng hoạt động vì phương thức Object.create () tạo một đối tượng mới với các thuộc tính và đối tượng nguyên mẫu được chỉ định.

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

var obj2 = Object.create(obj1);

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

obj1.x; //5

4
đây là một bản sao nông
Radagast the Brown

6

Đơn giản và cách nhanh nhất để sao chép một Object trong NodeJS là sử dụng phương thức 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"}

Phương thức Object.keys yêu cầu JavaScript 1.8.5; nodeJS v0.4.11 hỗ trợ phương thức này

nhưng tất nhiên đối với các đối tượng lồng nhau cần thực hiện func đệ quy


Giải pháp khác là sử dụng JSON gốc (Được triển khai trong JavaScript 1.7), nhưng nó chậm hơn nhiều (chậm hơn 10 lần) so với trước đây

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

5

Ngoài ra còn có một dự án trên Github nhằm mục đích trở thành một cổng trực tiếp hơn của jQuery.extend() :

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

Một ví dụ, được sửa đổi từ các tài liệu 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);


3

Tìm kiếm một lựa chọn nhân bản thực sự, tôi tình cờ tìm thấy liên kết vô lý đến đây:

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

Tôi đã sửa đổi giải pháp trên trang đó để chức năng được gắn vào Objectnguyên mẫu không thể đếm được. Đây là kết quả của tôi:

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

Hy vọng điều này sẽ giúp người khác là tốt. Lưu ý rằng có một số cảnh báo ... đặc biệt với các thuộc tính có tên là "bản sao". Công việc này tốt cho tôi. Tôi không nhận bất kỳ tín dụng cho việc viết nó. Một lần nữa, tôi chỉ thay đổi cách nó được xác định.


Cái này sai. Loại ngày là đối tượng để mã này sẽ thay thế ngày bằng các đối tượng trống ... Đừng sử dụng điều này.
jtblin

3

Bạn sẽ đau khổ nhưng giải pháp rất đơn giản.

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

var obj2 = {...obj1}; // Bùng nổ



0

Nếu bạn đang sử dụng tập lệnh cà phê, nó dễ như:

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

Mặc dù đây không phải là một bản sao sâu sắc.


0

Không có câu trả lời nào làm tôi hài lòng, một số câu trả lời không hoạt động hoặc chỉ là bản sao nông, câu trả lời từ @ clint-harris và sử dụng JSON.parse / stringify là tốt nhưng khá chậm. Tôi đã tìm thấy một mô-đun thực hiện nhân bản sâu nhanh: https://github.com/AlexeyKupershtokh/node-v8-clone


0

Không có cách dựng sẵn để thực hiện một bản sao thực sự (bản sao sâu) của một đối tượng trong node.js. Có một số trường hợp khó khăn vì vậy bạn chắc chắn nên sử dụng một thư viện cho việc này. Tôi đã viết một chức năng như vậy cho thư viện đơn giản của tôi . Bạn có thể sử dụng deepCopychức năng mà không cần sử dụng bất cứ thứ gì khác từ thư viện (khá nhỏ) nếu bạn không cần nó. Hàm này hỗ trợ nhân bản nhiều loại dữ liệu, bao gồm mảng, ngày tháng và biểu thức chính quy, nó hỗ trợ các tham chiếu đệ quy và nó cũng hoạt động với các đối tượng có hàm xây dựng có các tham số bắt buộc.

Đây là mã:

//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 nhanh nhất, nó mở phương thức clone gốc từ node.js

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

0

Một giải pháp khác là gói gọn trực tiếp trong biến mới bằng cách sử dụng: obj1= {...obj2}


Đây là một bản sao nông
Rémi Doolaeghe

0

Bạn cũng có thể sử dụng thư viện nhân bản này để các đối tượng nhân bản sâu.

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

const clonedObject = clone(sourceObject);

-2

Bạn có thể tạo đối tượng nguyên mẫu và sau đó gọi đối tượng đối tượng mỗi khi bạn muốn sử dụng và thay đổi đối tượng:

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

Bạn cũng có thể truyền đối số cho hàm tạo đối tượng

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

Hy vọng điều này là hữu ích.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.