【问题标题】:Nodejs: how to clone an objectNodejs:如何克隆对象
【发布时间】:2011-05-22 15:53:46
【问题描述】:

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

我想知道如何在nodejs中克隆一个对象。

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

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

    function clone(a) {
       return JSON.parse(JSON.stringify(a));
    }
    

    这不是唯一的答案,也不是最优雅的答案;对于生产瓶颈,应考虑所有其他答案。然而,这是一个快速而肮脏的解决方案,非常有效,并且在我将克隆一个简单的属性散列的大多数情况下很有用。

    【讨论】:

    • @djechlin 当然可以。试试看:jsfiddle.net/katowulf/E5jC3(使用节点 0.10.11 测试)它无法重构函数或原型数据,但它可以很好地获取值。
    • 这会将日期转换为字符串
    • @Backus 以及对象和函数。
    • @Kato,为什么循环引用意味着不需要进行深拷贝?他们是两个无关的东西。当然可以为 NodeJS 编写一个好的克隆方法,它支持循环引用并且不依赖于使用 JSON 进行克隆。 github.com/pvorb/node-clone
    • 已在 cmets 和描述中注明。你不能克隆一个函数、对象原型或其他你不应该尝试用通用克隆方法做的事情。你需要不止一行代码,而且可能不应该克隆那个垃圾——在你的类上放置一个克隆方法,该方法知道如何处理内部并执行newObj = obj.clone(args...);
    【解决方案2】:

    上述答案中均未提及 Object.assign。

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

    如果您使用的是 ES6,则可以使用扩展运算符:

    let cloned = { ... source };
    

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

    【讨论】:

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

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

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

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

    【讨论】:

      【解决方案4】:

      您也可以使用 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]);
      

      【讨论】:

        【解决方案5】:

        很难进行通用但有用的克隆操作,因为应该递归克隆什么以及应该复制什么取决于特定对象应该如何工作。

        可能有用的东西是

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

        在这段代码中,逻辑是

        • 如果是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);
        };
        

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

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

        【讨论】:

        • +1 表示对象需要自己实现 .clone 方法。这是处理克隆对象的最佳方式。
        • if (x.clone) 应该是if (typeof x.clone === 'function')
        • @YanickRochon:谢谢,已修复。抱歉,不知怎的,我之前没有注意到这条评论......
        【解决方案6】:

        没有用于克隆对象的本地方法。 Underscore 实现了_.clone,这是一个浅克隆。

        _.clone = function(obj) {
          return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
        };
        

        它要么切片,要么扩展它。

        这里是_.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;
        };
        

        Extend 只是遍历所有项目并创建一个包含项目的新对象。

        如果你愿意,你可以推出自己的幼稚实现

        function clone(o) {
          var ret = {};
          Object.keys(o).forEach(function (val) {
            ret[val] = o[val];
          });
          return ret;
        }
        

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

        我亲自问过一个关于deep cloning objects before 的问题,我得出的结论是你就是不这样做。

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

        【讨论】:

          【解决方案7】:

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

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

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

          【讨论】:

          • 简单、优雅、精彩。我期待您的其他 SO 贡献 ^_^
          【解决方案8】:

          老问题,但有一个比目前建议的更优雅的答案;使用内置的 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 } }
          

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

          使用上面的示例变量,您也可以这样做:

          var anotherMergeVar = { foo: "bar" };
          
          extend(copiedObject, { anotherParam: 'value' }, anotherMergeVar);
          
          console.log(copiedObject);
          
          // outputs:
          // { test: 12345, nested: { val: 6789 }, anotherParam: 'value', foo: 'bar' }
          

          非常方便的实用程序,尤其是在我习惯于在 AngularJS 和 jQuery 中扩展的地方。

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

          【讨论】:

          【解决方案9】:

          我实现了一个完整的深拷贝。我相信它是通用克隆方法的最佳选择,但它不处理循环引用。

          使用示例:

          parent = {'prop_chain':3}
          obj = Object.create(parent)
          obj.a=0; obj.b=1; obj.c=2;
          
          obj2 = copy(obj)
          
          console.log(obj, obj.prop_chain)
          // '{'a':0, 'b':1, 'c':2} 3
          console.log(obj2, obj2.prop_chain)
          // '{'a':0, 'b':1, 'c':2} 3
          
          parent.prop_chain=4
          obj2.a = 15
          
          console.log(obj, obj.prop_chain)
          // '{'a':0, 'b':1, 'c':2} 4
          console.log(obj2, obj2.prop_chain)
          // '{'a':15, 'b':1, 'c':2} 4
          

          代码本身:

          此代码复制对象及其原型,还复制函数(可能对某人有用)。

          function copy(obj) {
            // (F.prototype will hold the object prototype chain)
            function F() {}
            var newObj;
          
            if(typeof obj.clone === 'function')
              return obj.clone()
          
            // To copy something that is not an object, just return it:
            if(typeof obj !== 'object' && typeof obj !== 'function' || obj == null)
              return obj;
          
            if(typeof obj === 'object') {    
              // Copy the prototype:
              newObj = {}
              var proto = Object.getPrototypeOf(obj)
              Object.setPrototypeOf(newObj, proto)
            } else {
              // If the object is a function the function evaluate it:
              var aux
              newObj = eval('aux='+obj.toString())
              // And copy the prototype:
              newObj.prototype = obj.prototype
            }
          
            // Copy the object normal properties with a deep copy:
            for(var i in obj) {
              if(obj.hasOwnProperty(i)) {
                if(typeof obj[i] !== 'object')
                  newObj[i] = obj[i]
                else
                  newObj[i] = copy(obj[i])
              }
            }
          
            return newObj;
          }
          

          有了这个副本,我找不到原始副本和复制副本之间的任何区别,除非原始副本在其构造上使用了闭包,所以我认为它是一个很好的实现。

          希望对你有帮助

          【讨论】:

            【解决方案10】:

            根据你想对你的克隆对象做什么,你可以利用 javascript 的原型继承机制并通过以下方式实现一个有点克隆的对象:

            var clonedObject = Object.create(originalObject);
            

            请记住,这不是一个完整的克隆——无论好坏。

            这样做的好处是您实际上没有复制对象,因此内存占用会很低。

            关于这个方法需要记住的一些棘手的事情是,原型链中定义的属性的迭代有时会有点不同,而且对原始对象的任何更改也会影响克隆对象,除非该属性已设置也在自己身上。

            【讨论】:

            • 这太荒谬了,太危险了,根本不像克隆人。 var a = {foo: "bar"}, b = Object.create(a); a.foo = "broken"; console.log(b.foo); // "broken";
            • @naomik:b.foo = "working"; console.log(a.foo); // still "broken"; 当然必须知道原始对象的更改将反映在“克隆”中,而“克隆”中的更改不会反映在原始对象——但我不会称之为危险——这完全取决于你想用你的克隆做什么
            • @naomik:我对 JS 很陌生,所以我想要一些精度。 olli-k 提出的浅层克隆和您喜欢的似乎(恕我直言,但我可能错了)与 voxpelli 提出的解决方案具有相同的限制。原始和浅层关闭也将共享相同的“项目”,更新原始项目中的项目也会影响浅层克隆。还是?
            • @bmornin,如果您使用 Olli K 的技术,修改原始对象不会导致新对象发生变化。
            • @VoxPelli “克隆”的全部目的是隔离原始对象的更改,因此这种方法更危险。
            【解决方案11】:

            JavaScript 中的对象和数组使用引用调用,如果您更新复制的值,它可能会反映在原始对象上。 为了防止这种情况,您可以使用 lodash 库 cloneDeep 方法深度克隆对象,以防止传递引用 运行命令

            npm install lodash

            const ld = require('lodash')
            const objectToCopy = {name: "john", age: 24}
            const clonedObject = ld.cloneDeep(objectToCopy)
            

            【讨论】:

              【解决方案12】:

              对于数组,可以使用

              var arr = [1,2,3]; 
              var arr_2 = arr ; 
              
              print ( arr_2 ); 
              

              arr=arr.slice(0);

              print ( arr ); 
              
              arr[1]=9999; 
              
              print ( arr_2 ); 
              

              【讨论】:

                猜你喜欢
                • 2021-11-01
                • 2010-11-08
                • 2012-11-17
                • 2010-09-07
                • 2012-12-07
                • 1970-01-01
                • 2011-11-07
                • 2017-05-22
                • 1970-01-01
                相关资源
                最近更新 更多