在Node.js中克隆对象


203

在node.js中克隆对象的最佳方法是什么

例如,我想避免以下情况:

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

该对象很可能包含复杂的类型作为属性,因此无法解决简单的for(obj1中的var x)。我需要自己编写一个递归克隆吗,还是内置了一些我看不到的东西?


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

4
请注意,在上面@SalmanPK的注释中,这是一个浅表克隆。因此它适用于slifty的示例,但是如果存在嵌套的数组或对象,它们将是引用。:/
Jesse 2012年

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

Answers:


298

可能性1

简洁的深层副本:

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

可能性2(已弃用)

注意:Node.js文档中,此解决方案现已标记为已弃用:

从未打算在内部Node.js模块外部使用util._extend()方法。社区仍然找到并使用了它。

它已被弃用,不应在新代码中使用。JavaScript通过Object.assign()具有非常相似的内置功能。

原始答案

对于浅表副本,请使用Node的内置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

Node _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
这个问题专门要求递归克隆。这是一个浅表克隆。
本杰明·阿特金

28
这个名称难道不_*应该意味着它是私有方法,不应该被依赖吗?
2013年

7
每个大小不限的JavaScript项目都有一个或多个extend()实现,Node也不例外。Node.js核心广泛使用了此功能。引用艾萨克斯的话说:“这不会很快发生。”
jimbo

2
对我来说效果很好。比摆弄对象原型IMO好得多
迈克尔Dausmann

12
这是错误的答案。根据节点的文档: nodejs.org/api/util.html#util_util_extend_obj util._extend()方法从未打算在内部Node.js模块之外使用。社区仍然找到并使用了它。 它已被弃用,不应在新代码中使用。JavaScript具有非常相似的内置功能,直到Object.assign().
Jordie

264

我很惊讶Object.assign没有被提及。

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

如果可用(例如Babel),则可以使用对象传播运算符

let cloned = { ... source };

1
你救了我的一天!谢谢
wzr1337 '16

2
与必须导入第三方库或使用不完善的JSON解决方法相比,这是一个更好的解决方案。谢谢!
Neil S

75
这是一个浅表副本
Jordan Davidson

14
警告深层克隆,对于深层克隆仍然需要使用其他替代方法。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…–
gsalgadotoledo

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

这将定义您可以使用的扩展方法。代码来自本文。


我不知道这应该如何工作。它修改了原始对象!我应该如何使用此功能来获取对象的副本?您可以在此处添加一些使用代码吗?在阅读了您的文章和博客文章之后,我仍然无法弄清楚如何将其用于克隆对象。
布拉德(Brad)2012年

3
这真的有效吗?“ if(名称在dest中)”-仅在属性已经存在于dest中时才更改属性。它应该被否定。
记忆式的2012年

8
修改Object.prototype是否应该是僵尸程序吗?另外,该文章链接已损坏。
丹尼尔·谢弗

刚刚尝试了文章链接,它对我有用。尝试时可能是网络故障。
迈克尔·狄龙

基于大量评论,我更新了答案,使其包括一个不会添加到对象原型中的变体。
Shamasis Bhattacharya 2014年

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

2
现有答案中已经提出了一点,没有重复之处。
暗影巫师为您耳边2013年

@ShadowWizard这些是不同的方法。这个简单地转换为json并返回到对象,而链接的答案则用于Object.keys()遍历对象
mente

这个答案是错误的。JSON.stringify接受一个日期对象并将其简化为字符串,然后在对其进行解析后将其保留为字符串,以便它改变对象的状态,从而为您保留与日期不同的对象。
twboc



8

如果不想“自己动手”,这里有一些Node模块。这个看起来不错:https//www.npmjs.com/package/clone

看起来它可以处理各种内容,包括循环引用。从github页面:

clone master克隆对象,数组,Date对象和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支持此方法

但是对于嵌套对象,当然需要实现递归函数


另一种解决方案是使用本机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 docs修改而来:

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

在寻找真正的克隆选项时,我偶然发现了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;
    }
});

希望这对其他人也有帮助。请注意,有一些警告...尤其是对于名为“ clone”的属性。这对我来说很好。我不以为然。同样,我只更改了定义方式。


错了 日期类型是对象,因此此代码将用空对象替换日期。请勿使用此类型。
jtblin

3

你们都很痛苦,但解决方案很简单。

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

var obj2 = {...obj1}; //景气



0

如果您使用的是咖啡脚本,则非常简单:

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

虽然这不是很深的克隆。



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

希望这会有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.