【问题标题】:Cloning an Object in Node.js在 Node.js 中克隆对象
【发布时间】:2011-06-30 15:45:12
【问题描述】:

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

例如我想避免以下情况:

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

该对象很可能包含复杂类型作为属性,因此简单的 for(var x in obj1) 无法解决。我是否需要自己编写一个递归克隆,或者是否有一些我没有看到的内置内容?

【问题讨论】:

  • 1. npm install underscore 2. var _ = require('underscore') 3. _.clone(objToClone);
  • 请注意,在上面@SalmanPK 的评论中,这是一个 shallow 克隆。所以它适用于 slifty 的例子,但如果有嵌套的数组或对象,它们将是引用。 ://
  • 我发现这篇文章很有帮助:heyjavascript.com/4-creative-ways-to-clone-objects
  • @Jordan Hudson - 在第二个示例中很好地使用了 JSON。 var newObj = JSON.parse(JSON.stringify(oldObj)); //现在newObj是一个克隆。唯一的问题是 stringify 不适用于递归引用,所以需要小心。

标签: javascript node.js


【解决方案1】:

您可以使用 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

【讨论】:

    【解决方案2】:
    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;
        }
    });
    

    这将定义一个可以使用的扩展方法。代码来from this article.

    【讨论】:

    • 我不明白这应该如何工作。它修改了原始对象!我应该如何使用此函数来获取对象的克隆?你能在这里添加一些使用代码吗?在阅读了您的帖子和博文后,我仍然无法弄清楚它是如何用于克隆对象的。
    • 这真的有效吗? "if (name in dest)" - 只有在 dest 中已经存在时才会更改该属性。它应该被否定。
    • 修改 Object.prototype 不应该被禁止吗?该文章链接也已损坏。
    • 刚刚尝试了文章链接,它对我有用。当您尝试它时,可能是网络故障。
    • 基于许多 cmets,我更新了答案以包含一个不添加到对象原型的变体。
    【解决方案3】:

    在 NodeJS 中克隆 Object 的简单且最快的方法是使用 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;
    

    【讨论】:

      【解决方案4】:

      如果您使用的是 coffee-script,那么简单:

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

      虽然这不是深度克隆。

      【讨论】:

        【解决方案5】:

        如果不想“自行开发”,可以使用一些 Node 模块。这个好看:https://www.npmjs.com/package/clone

        看起来它可以处理各种事情,包括循环引用。来自github 页面:

        克隆大师克隆对象、数组、日期对象和正则表达式 对象。一切都是递归克隆的,所以你可以克隆日期 例如,在对象的数组中。 [...] 循环引用?是的!

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

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

            希望这对其他人也有帮助。请注意,有一些警告......特别是对于名为“克隆”的属性。这对我很有效。我不认为写它有任何功劳。同样,我只是更改了它的定义方式。

            【讨论】:

            • 这是错误的。日期类型是对象,因此此代码将用空对象替换日期...不要使用它。
            【解决方案8】:

            查看underscore.js。它同时具有cloneextend 以及许多其他非常有用的功能。

            这很有用:Using the Underscore module with Node.js

            【讨论】:

              【解决方案9】:

              可能性 1

              简单的深拷贝:

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

              可能性 2(已弃用)

              注意:此解决方案现已在documentation of Node.js 中标记为已弃用:

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

              它已被弃用,不应在新代码中使用。 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;
              };
              

              【讨论】:

              • 这个问题专门要求递归克隆。这是一个浅层克隆。
              • _* 这样的名字不是应该意味着它是一个私有方法,不应该被依赖吗?
              • 任何规模的 JavaScript 项目都有一个或多个 extend() 实现,Node 也不例外。 Node.js 核心广泛使用了这个功能。引用 Isaacs,"It's not going anywhere any time soon."
              • 非常适合我。比在 imo 中搞乱对象原型要好得多
              • 这是错误的答案。根据节点的文档:nodejs.org/api/util.html#util_util_extend_obj util._extend() 方法从未打算在内部 Node.js 模块之外使用。社区还是找到并使用了它。 它已被弃用,不应在新代码中使用。 JavaScript 通过 Object.assign(). 提供了非常相似的内置功能
              【解决方案10】:
              var obj2 = JSON.parse(JSON.stringify(obj1));
              

              【讨论】:

              • 这已经在this existing answer 中提出了建议,无需重复。
              • @ShadowWizard 这些是不同的方法。这个简单地转换为 json 并返回到对象,而链接的答案使用 Object.keys() 来遍历对象
              • 这个答案是错误的。 JSON.stringify 接受一个日期对象并将其简化为一个字符串,然后在解析后将其保留为一个字符串,因此它会改变对象的状态,从而为您留下与最初日期不同的对象。
              【解决方案11】:

              没有一个答案让我满意,有几个不起作用或只是浅层克隆,来自@clint-harris 和使用 JSON.parse/stringify 的答案很好但很慢。我发现了一个可以快速进行深度克隆的模块:https://github.com/AlexeyKupershtokh/node-v8-clone

              【讨论】:

                【解决方案12】:

                你也可以在 NodeJS 中使用 SugarJS。

                http://sugarjs.com/

                他们有一个非常干净的克隆功能: http://sugarjs.com/api/Object/clone

                【讨论】:

                  【解决方案13】:

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

                  【讨论】:

                    【解决方案14】:
                    npm install node-v8-clone
                    

                    最快的克隆器,它从 node.js 中打开本地克隆方法

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

                    【讨论】:

                      【解决方案15】:

                      还有另一个库lodash,它有clonecloneDeep

                      clone 将克隆您的对象,但不会为非原始值创建新实例,而是使用对原始对象的引用

                      cloneDeep 将在不引用原始对象的情况下创建真正的新对象,因此当您之后必须更改对象时会更安全。

                      【讨论】:

                        【解决方案16】:

                        此代码也是有效的,因为 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
                        

                        【讨论】:

                        • 这是浅拷贝
                        【解决方案17】:

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

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

                        如果可用(例如 Babel),您可以使用object spread operator

                        let cloned = { ... source };
                        

                        【讨论】:

                        【解决方案18】:

                        你可以原型化对象,然后每次你想使用和改变对象时调用对象实例:

                        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
                        

                        希望这有帮助。

                        【讨论】:

                          【解决方案19】:

                          另一种解决方案是使用以下方法直接封装在新变量中: obj1= {...obj2}

                          【讨论】:

                          • 这是浅拷贝
                          【解决方案20】:

                          你们都在受苦,但解决办法很简单。

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

                          var obj2 = {...obj1}; // 轰隆隆

                          【讨论】:

                          • 这会创建一个 obj1 的浅拷贝
                          【解决方案21】:

                          您也可以使用这个 clone 库来深度克隆对象。

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

                          【讨论】:

                            【解决方案22】:

                            如果您使用的是普通对象和数组,并且不关心克隆函数或递归引用,这里有一个简单的deepClone 实现,它适用于普通对象、数组、字符串、数字、正则表达式、日期等.

                            // Simple Deep Clone
                            // Does not clone functions or handle recursive references.
                            function deepClone(original) {
                              if (original instanceof RegExp) {
                                return new RegExp(original);
                              } else if (original instanceof Date) {
                                return new Date(original.getTime());
                              } else if (Array.isArray(original)) {
                                return original.map(deepClone);
                              } else if (typeof original === 'object' && original !== null) {
                                const clone = {};
                                Object.keys(original).forEach(k => {
                                  clone[k] = deepClone(original[k]);
                                });
                                return clone;
                              }
                              return original;
                            }
                            
                            // Usage:
                            
                            const obj = { n: 1, a: [ { a: 1 }, { a: 2 } ], d: new Date(), s: 'foo' };
                            const clone = deepClone(obj);
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2014-03-07
                              • 2011-02-04
                              • 2010-09-18
                              • 2020-11-04
                              • 2011-07-18
                              • 1970-01-01
                              相关资源
                              最近更新 更多