Nodejs:如何克隆对象

gui*_* 桂林 88 javascript node.js

如果我克隆一个数组,我会使用 cloneArr = arr.slice()

我想知道如何克隆nodejs中的对象.

Kat*_*ato 170

对于不需要挤压每一滴性能的实用程序和类,我经常作弊并只使用JSON来执行深层复制:

function clone(a) {
   return JSON.parse(JSON.stringify(a));
}
Run Code Online (Sandbox Code Playgroud)

这不是唯一的答案或最优雅的答案; 所有其他答案都应该考虑到生产瓶颈.但是,这是一个快速而肮脏的解决方案,非常有效,并且在我克隆一个简单的属性哈希的大多数情况下都很有用.

  • 这会将日期转换为字符串 (11认同)
  • @Backus以及对象和功能. (4认同)
  • @djechlin当然可以.尝试一下:http://jsfiddle.net/katowulf/E5jC3/(用节点0.10.11测试)它无法重建函数或原型数据,但它可以获得周围的值. (2认同)
  • @Kato,为什么循环引用意味着没有必要制作深拷贝?他们是两个无关的东西.为NodeJS编写一个支持循环引用的良好克隆方法当然是可能的,并且不会依赖于使用JSON进行克隆.https://github.com/pvorb/node-clone (2认同)
  • 已经在注释和说明中指出。您不能克隆函数,对象原型或其他您不应该尝试使用通用克隆方法进行的操作。您将需要多于一行代码,并且无论如何都不应该克隆该垃圾-在您的类上放置一个克隆方法,该方法知道如何处理内部信息并执行`newObj = obj.clone(args。 ..);` (2认同)

Sam*_*m G 31

在上述任何答案中都没有提到Object.assign.

let cloned = Object.assign({}, source);
Run Code Online (Sandbox Code Playgroud)

如果你在ES6上,你可以使用传播运算符:

let cloned = { ... source };
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,这两个都是浅克隆,对象上的扩展运算符需要node.js 8+. (20认同)
  • 正如@jlh 提到的,这不适用于嵌套对象。您可以在此处阅读有关此方法的深度克隆问题的更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Deep_Clone (2认同)
  • 正如 Shnd 所说,这个答案是不正确的。对于深度克隆,我们需要使用其他替代方案,因为 Object.assign() 会复制属性值。如果源值是对对象的引用,则它仅复制该引用值。 (2认同)
  • 这不会克隆,它只是引用原始对象,这意味着如果您更改某些内容,它将转到主对象 (2认同)

Cli*_*ris 20

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

看起来它处理各种东西,包括循环引用.从github页面:

clone masters克隆对象,数组,Date对象和RegEx对象.例如,所有东西都是递归克隆的,因此您可以在对象的数组中克隆日期.[...]循环参考?是的!


Jon*_*zio 14

structuredClone()Node.js 17.x 中添加了允许深度克隆的方法。

参考文档:https://developer.mozilla.org/en-US/docs/Web/API/structuredClone


650*_*502 10

很难做一个通用但有用的克隆操作,因为应该递归克隆的内容以及应该复制的内容取决于特定对象应该如何工作.

可能有用的东西是

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;
}
Run Code Online (Sandbox Code Playgroud)

在这段代码中,逻辑是

  • 如果是nullundefined只是返回相同(需要特殊情况,因为尝试查看clone方法是否存在是错误的)
  • 对象有一个clone方法吗?然后使用它
  • 对象是一个数组?然后进行递归克隆操作
  • 否则只返回相同的值

这个克隆函数应该允许轻松实现自定义克隆方法...例如

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);
};
Run Code Online (Sandbox Code Playgroud)

在对象中,您知道对于特定数组的正确克隆操作只是一个浅拷贝,那么您可以调用values.slice()而不是clone(values).

例如,在上面的代码中,我明确要求克隆多边形对象将克隆点,但将共享相同的样式对象.如果我想要克隆样式对象,那么我就可以通过clone(this.style).

  • `if(x.clone)`应该是`if(typeof x.clone ==='function')` (3认同)

Ray*_*nos 9

没有用于克隆对象的本机方法.Underscore实现_.clone了一个浅层克隆.

_.clone = function(obj) {
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
Run Code Online (Sandbox Code Playgroud)

它可以切片或延伸它.

这里的 _.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;
};
Run Code Online (Sandbox Code Playgroud)

扩展只是遍历所有项目并创建一个包含其中项目的新对象.

如果需要,您可以推出自己的天真实现

function clone(o) {
  var ret = {};
  Object.keys(o).forEach(function (val) {
    ret[val] = o[val];
  });
  return ret;
}
Run Code Online (Sandbox Code Playgroud)

有充分的理由避免深度克隆,因为无法克隆封闭.

我个人问了一个问题deep cloning objects before,我得出的结论是你不这样做.

我的建议是使用underscore它和_.clone浅克隆的方法


小智 9

对于浅拷贝,我喜欢使用reduce模式(通常在模块中等),如下所示:

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

这里有几个选项的jsperf:http://jsperf.com/shallow-copying


Sco*_*ers 7

老问题,但到目前为止,有一个更优雅的答案; 使用内置的utils._extend:

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 } }
Run Code Online (Sandbox Code Playgroud)

请注意第一个参数与空对象{}的使用 - 这告诉扩展复制的对象需要复制到新对象.如果使用现有对象作为第一个参数,则第二个(以及所有后续)参数将在第一个参数变量上进行深度合并复制.

使用上面的示例变量,您还可以执行以下操作:

var anotherMergeVar = { foo: "bar" };

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

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 }, anotherParam: 'value', foo: 'bar' }
Run Code Online (Sandbox Code Playgroud)

非常方便的实用程序,特别是我习惯在AngularJS和jQuery中扩展的地方.

希望这有助于其他人; 对象引用覆盖是一种痛苦,每次都解决它!

  • 使用 _extend 现在已折旧。使用 Object.assign() 代替:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign (2认同)

Mic*_*hal 6

你也可以使用lodash.它有一个clonecloneDeep方法.

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]);
Run Code Online (Sandbox Code Playgroud)