首页 文章

如何正确克隆JavaScript对象?

提问于
浏览
2569

我有一个对象, x . 我想将其复制为对象 y ,这样对 y 的更改不会修改 x . 我意识到复制从内置JavaScript对象派生的对象将导致额外的,不需要的属性 . 这不是复制我自己的一个文字构造的对象 .

如何正确克隆JavaScript对象?

30 回答

  • 14
    let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)
    

    ES6解决方案,如果你想(浅)克隆一个 class instance 而不仅仅是一个属性对象 .

  • 5

    互联网上的大多数解决方案存在几个问题 . 所以我决定进行跟进,其中包括为什么接受的答案不应被接受 .

    出发情况

    我想要 deep-copy 一个Javascript Object 与它的所有孩子和他们的孩子等等 . 但由于我不是一个普通的开发人员,我的 Object 正常 propertiescircular structures 甚至 nested objects .

    所以让我们首先创建一个 circular structure 和一个 nested object .

    function Circ() {
        this.me = this;
    }
    
    function Nested(y) {
        this.y = y;
    }
    

    让我们把所有东西放在一个名为 a 的_210662中 .

    var a = {
        x: 'a',
        circ: new Circ(),
        nested: new Nested('a')
    };
    

    接下来,我们要将 a 复制到名为 b 的变量中并对其进行变异 .

    var b = a;
    
    b.x = 'b';
    b.nested.y = 'b';
    

    你知道在这里发生了什么,因为如果不是你甚至不会落在这个伟大的问题上 .

    console.log(a, b);
    
    a --> Object {
        x: "b",
        circ: Circ {
            me: Circ { ... }
        },
        nested: Nested {
            y: "b"
        }
    }
    
    b --> Object {
        x: "b",
        circ: Circ {
            me: Circ { ... }
        },
        nested: Nested {
            y: "b"
        }
    }
    

    现在让我们找到一个解决方案 .

    JSON

    我尝试的第一次尝试是使用 JSON .

    var b = JSON.parse( JSON.stringify( a ) );
    
    b.x = 'b';
    b.nested.y = 'b';
    

    唐't waste too much time on it, you' ll得到 TypeError: Converting circular structure to JSON .

    递归副本(接受的“答案”)

    让我们来看看接受的答案 .

    function cloneSO(obj) {
        // Handle the 3 simple types, and null or undefined
        if (null == obj || "object" != typeof obj) return obj;
    
        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
    
        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = cloneSO(obj[i]);
            }
            return copy;
        }
    
        // Handle Object
        if (obj instanceof Object) {
            var copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
            }
            return copy;
        }
    
        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    

    看起来不错,嘿?它是对象的递归副本,也处理其他类型,如 Date ,但这不是必需的 .

    var b = cloneSO(a);
    
    b.x = 'b';
    b.nested.y = 'b';
    

    递归和 circular structures 不能很好地协同工作...... RangeError: Maximum call stack size exceeded

    原生解决方案

    在和我的同事争吵之后,我的老板问我们发生了什么,他在谷歌搜索后发现了一个简单的解决方案 . 它被称为 Object.create .

    var b = Object.create(a);
    
    b.x = 'b';
    b.nested.y = 'b';
    

    这个解决方案在前一段时间被添加到Javascript中,甚至可以处理 circular structure .

    console.log(a, b);
    
    a --> Object {
        x: "a",
        circ: Circ {
            me: Circ { ... }
        },
        nested: Nested {
            y: "b"
        }
    }
    
    b --> Object {
        x: "b",
        circ: Circ {
            me: Circ { ... }
        },
        nested: Nested {
            y: "b"
        }
    }
    

    ...而且你看,它不适用于里面的嵌套结构 .

    本地解决方案的polyfill

    在旧浏览器中有一个 Object.create 的polyfill就像IE 8一样 . 它's something like recommended by Mozilla, and of course, it'不完美,导致与原生解决方案相同的问题 .

    function F() {};
    function clonePF(o) {
        F.prototype = o;
        return new F();
    }
    
    var b = clonePF(a);
    
    b.x = 'b';
    b.nested.y = 'b';
    

    我把 F 放在了范围之外,所以我们可以看一下 instanceof 告诉我们的内容 .

    console.log(a, b);
    
    a --> Object {
        x: "a",
        circ: Circ {
            me: Circ { ... }
        },
        nested: Nested {
            y: "b"
        }
    }
    
    b --> F {
        x: "b",
        circ: Circ {
            me: Circ { ... }
        },
        nested: Nested {
            y: "b"
        }
    }
    
    console.log(typeof a, typeof b);
    
    a --> object
    b --> object
    
    console.log(a instanceof Object, b instanceof Object);
    
    a --> true
    b --> true
    
    console.log(a instanceof F, b instanceof F);
    
    a --> false
    b --> true
    

    与本机解决方案相同的问题,但输出稍差 .

    更好(但不完美)的解决方案

    在挖掘时,我发现了一个类似的问题(In Javascript, when performing a deep copy, how do I avoid a cycle, due to a property being "this"?)到这个,但有一个更好的解决方案 .

    function cloneDR(o) {
        const gdcc = "__getDeepCircularCopy__";
        if (o !== Object(o)) {
            return o; // primitive value
        }
    
        var set = gdcc in o,
            cache = o[gdcc],
            result;
        if (set && typeof cache == "function") {
            return cache();
        }
        // else
        o[gdcc] = function() { return result; }; // overwrite
        if (o instanceof Array) {
            result = [];
            for (var i=0; i<o.length; i++) {
                result[i] = cloneDR(o[i]);
            }
        } else {
            result = {};
            for (var prop in o)
                if (prop != gdcc)
                    result[prop] = cloneDR(o[prop]);
                else if (set)
                    result[prop] = cloneDR(cache);
        }
        if (set) {
            o[gdcc] = cache; // reset
        } else {
            delete o[gdcc]; // unset again
        }
        return result;
    }
    
    var b = cloneDR(a);
    
    b.x = 'b';
    b.nested.y = 'b';
    

    让我们来看看输出......

    console.log(a, b);
    
    a --> Object {
        x: "a",
        circ: Object {
            me: Object { ... }
        },
        nested: Object {
            y: "a"
        }
    }
    
    b --> Object {
        x: "b",
        circ: Object {
            me: Object { ... }
        },
        nested: Object {
            y: "b"
        }
    }
    
    console.log(typeof a, typeof b);
    
    a --> object
    b --> object
    
    console.log(a instanceof Object, b instanceof Object);
    
    a --> true
    b --> true
    
    console.log(a instanceof F, b instanceof F);
    
    a --> false
    b --> false
    

    需求是匹配的,但仍然存在一些较小的问题,包括将 nestedinstancecirc 更改为 Object .

    共享叶子的树的结构将不会被复制,它们将成为两个独立的叶子:

    [Object]                     [Object]
             /    \                       /    \
            /      \                     /      \
          |/_      _\|                 |/_      _\|  
      [Object]    [Object]   ===>  [Object]    [Object]
           \        /                 |           |
            \      /                  |           |
            _\|  |/_                 \|/         \|/
            [Object]               [Object]    [Object]
    

    结论

    使用递归和缓存的最后一个解决方案可能不是最好的,但它是对象的深度副本 . 它处理简单的 propertiescircular structuresnested object ,但它会在克隆时弄乱它们的实例 .

    jsfiddle

  • 763

    在ECMAScript 6中有Object.assign方法,它将所有可枚举的自有属性的值从一个对象复制到另一个对象 . 例如:

    var x = {myProp: "value"};
    var y = Object.assign({}, x);
    

    但请注意,嵌套对象仍会被复制为引用 .

  • 73

    使用jQuery,您可以使用extend shallow copy

    var copiedObject = jQuery.extend({}, originalObject)
    

    对copiedObject的后续更改不会影响originalObject,反之亦然 .

    或制作 deep copy

    var copiedObject = jQuery.extend(true, {}, originalObject)
    
  • 6

    有关W3C的"Safe passing of structured data"算法,请参阅http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data,该算法旨在由浏览器实现,以便将数据传递给Web worker . 但是,它有一些限制,因为它不处理功能 . 有关更多信息,请参阅https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm,包括JS中的替代算法,它可以帮助您了解其中的一部分 .

  • 5

    对旧问题的新答案!如果您有幸使用ECMAScript 2016(ES6)和Spread Syntax,那很容易 .

    keepMeTheSame = {first: "Me!", second: "You!"};
    cloned = {...keepMeTheSame}
    

    这为对象的浅拷贝提供了一种干净的方法 . 制作深层复制,意味着在每个递归嵌套对象中设置每个值的新副本,需要上面较重的解决方案 .

    JavaScript不断发展 .

  • 5

    我写了自己的实现 . 不确定它是否算作更好的解决方案:

    /*
        a function for deep cloning objects that contains other nested objects and circular structures.
        objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                        index (z)
                                             |
                                             |
                                             |
                                             |
                                             |
                                             |                      depth (x)
                                             |_ _ _ _ _ _ _ _ _ _ _ _
                                            /_/_/_/_/_/_/_/_/_/
                                           /_/_/_/_/_/_/_/_/_/
                                          /_/_/_/_/_/_/...../
                                         /................./
                                        /.....            /
                                       /                 /
                                      /------------------
                object length (y)    /
    */
    

    以下是实施:

    function deepClone(obj) {
        var depth = -1;
        var arr = [];
        return clone(obj, arr, depth);
    }
    
    /**
     *
     * @param obj source object
     * @param arr 3D array to store the references to objects
     * @param depth depth of the current object relative to the passed 'obj'
     * @returns {*}
     */
    function clone(obj, arr, depth){
        if (typeof obj !== "object") {
            return obj;
        }
    
        var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'
    
        var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
        if(result instanceof Array){
            result.length = length;
        }
    
        depth++; // depth is increased because we entered an object here
    
        arr[depth] = []; // this is the x-axis, each index here is the depth
        arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
        // start the depth at current and go down, cyclic structures won't form on depths more than the current one
        for(var x = depth; x >= 0; x--){
            // loop only if the array at this depth and length already have elements
            if(arr[x][length]){
                for(var index = 0; index < arr[x][length].length; index++){
                    if(obj === arr[x][length][index]){
                        return obj;
                    }
                }
            }
        }
    
        arr[depth][length].push(obj); // store the object in the array at the current depth and length
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
        }
    
        return result;
    }
    
  • 10

    对于那些使用AngularJS的人来说,还有直接的方法来克隆或扩展这个库中的对象 .

    var destination = angular.copy(source);
    

    要么

    angular.copy(source, destination);
    

    更多angular.copy documentation ...

  • 4

    使用Lodash:

    var y = _.clone(x, true);
    
  • 37

    如果你对浅拷贝没问题,那么underscore.js库有一个clone方法 .

    y = _.clone(x);
    

    或者你可以扩展它

    copiedObject = _.extend({},originalObject);
    
  • 68

    在一行代码中克隆Javascript对象的优雅方法

    Object.assign 方法是ECMAScript 2015(ES6)标准的一部分,完全符合您的要求需要 .

    var clone = Object.assign({}, obj);
    

    Object.assign()方法用于将所有可枚举的自有属性的值从一个或多个源对象复制到目标对象 .

    Read more...

    polyfill 支持旧浏览器:

    if (!Object.assign) {
      Object.defineProperty(Object, 'assign', {
        enumerable: false,
        configurable: true,
        writable: true,
        value: function(target) {
          'use strict';
          if (target === undefined || target === null) {
            throw new TypeError('Cannot convert first argument to object');
          }
    
          var to = Object(target);
          for (var i = 1; i < arguments.length; i++) {
            var nextSource = arguments[i];
            if (nextSource === undefined || nextSource === null) {
              continue;
            }
            nextSource = Object(nextSource);
    
            var keysArray = Object.keys(nextSource);
            for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
              var nextKey = keysArray[nextIndex];
              var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
              if (desc !== undefined && desc.enumerable) {
                to[nextKey] = nextSource[nextKey];
              }
            }
          }
          return to;
        }
      });
    }
    
  • 23

    MDN

    • 如果你想要浅拷贝,请使用 Object.assign({}, a)

    • 对于"deep"副本,请使用 JSON.parse(JSON.stringify(a))

    不需要外部库,但需要检查browser compatibility first .

  • 6

    有很多答案,但没有提到ECMAScript 5中的Object.create,它确实没有给你一个精确的副本,但是将源设置为新对象的原型 .

    因此,这不是问题的确切答案,但它是一个单行解决方案,因而优雅 . 它适用于2种情况:

    • 这种继承有用(呃!)

    • 不会修改源对象,从而使两个对象之间的关系成为非问题 .

    例:

    var foo = { a : 1 };
    var bar = Object.create(foo);
    foo.a; // 1
    bar.a; // 1
    foo.a = 2;
    bar.a; // 2 - prototype changed
    bar.a = 3;
    foo.a; // Still 2, since setting bar.a makes it an "own" property
    

    为什么我认为这个解决方案更优越?它是原生的,因此没有循环,没有递归 . 但是,旧版浏览器需要使用polyfill .

  • 559

    您可以简单地使用spread property来复制没有引用的对象 . 但是要小心(参见注释),'copy'只是在最低的对象/数组级别 . 嵌套属性仍然是引用!


    Complete clone:

    let x = {a: 'value1'}
    let x2 = {...x}
    
    // => mutate without references:
    
    x2.a = 'value2'
    console.log(x.a)    // => 'value1'
    

    Clone with references on second level:

    const y = {a: {b: 'value3'}}
    const y2 = {...y}
    
    // => nested object is still a references:
    
    y2.a.b = 'value4'
    console.log(y.a.b)    // => 'value4'
    

    JavaScript本身实际上不支持深度克隆 . 使用实用程序功能 . 例如Ramda:

    http://ramdajs.com/docs/#clone

  • 9

    JanTotoň的答案非常接近,由于兼容性问题,可能是最好在浏览器中使用,但它可能会导致一些奇怪的枚举问题 . 例如,执行:

    for ( var i in someArray ) { ... }
    

    迭代遍历数组的元素后,将clone()方法赋给i . 这是一个避免枚举并适用于node.js的改编:

    Object.defineProperty( Object.prototype, "clone", {
        value: function() {
            if ( this.cloneNode )
            {
                return this.cloneNode( true );
            }
    
            var copy = this instanceof Array ? [] : {};
            for( var attr in this )
            {
                if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
                {
                    copy[ attr ] = this[ attr ];
                }
                else if ( this[ attr ] == this )
                {
                    copy[ attr ] = copy;
                }
                else
                {
                    copy[ attr ] = this[ attr ].clone();
                }
            }
            return copy;
        }
    });
    
    Object.defineProperty( Date.prototype, "clone", {
        value: function() {
            var copy = new Date();
            copy.setTime( this.getTime() );
            return copy;
        }
    });
    
    Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
    Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
    Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );
    

    这避免了使clone()方法可枚举,因为defineProperty()默认为可枚举为false .

  • 110

    这是您可以使用的功能 .

    function clone(obj) {
        if(obj == null || typeof(obj) != 'object')
            return obj;    
        var temp = new obj.constructor(); 
        for(var key in obj)
            temp[key] = clone(obj[key]);    
        return temp;
    }
    
  • 26

    如果您不在对象中使用函数,则可以使用以下非常简单的内容:

    var cloneOfA = JSON.parse(JSON.stringify(a));
    

    这适用于包含对象,数组,字符串,布尔值和数字的所有类型的对象 .

    另请参阅this article about the structured clone algorithm of browsers,这是在向工作人员发送消息和从工作人员发布消息时使用的 . 它还包含深度克隆功能 .

  • 141

    来自这篇文章:Brian Huisman的How to copy arrays and objects in Javascript

    Object.prototype.clone = function() {
      var newObj = (this instanceof Array) ? [] : {};
      for (var 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;
    };
    
  • 13

    在ECMAScript 2018中

    let objClone = { ...obj };
    

    请注意 nested objects 仍然被复制 as a reference.

  • 18

    一个特别不优雅的解决方案是使用JSON编码来制作没有成员方法的对象的深层副本 . 方法是对您的目标对象进行JSON编码,然后通过对其进行解码,您将获得所需的副本 . 您可以根据需要进行多次解码,然后根据需要进行复制 .

    当然,函数不属于JSON,因此这仅适用于没有成员方法的对象 .

    这种方法非常适合我的用例,因为我将JSON blob存储在键值存储中,当它们作为JavaScript API中的对象公开时,每个对象实际上都包含对象原始状态的副本,所以我们可以在调用者突变暴露的对象后计算增量 .

    var object1 = {key:"value"};
    var object2 = object1;
    
    object2 = JSON.stringify(object1);
    object2 = JSON.parse(object2);
    
    object2.key = "a change";
    console.log(object1);// returns value
    
  • 10

    A.Levy的答案几乎完成,这是我的小贡献: there is a way how to handle recursive references ,看到这一行

    if(this[attr]==this) copy[attr] = copy;

    如果对象是XML DOM元素,我们必须使用 cloneNode

    if(this.cloneNode) return this.cloneNode(true);

    受A.Levy详尽的研究和Calvin的原型设计方法的启发,我提供了这个解决方案:

    Object.prototype.clone = function() {
      if(this.cloneNode) return this.cloneNode(true);
      var copy = this instanceof Array ? [] : {};
      for(var attr in this) {
        if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
          copy[attr] = this[attr];
        else if(this[attr]==this) copy[attr] = copy;
        else copy[attr] = this[attr].clone();
      }
      return copy;
    }
    
    Date.prototype.clone = function() {
      var copy = new Date();
      copy.setTime(this.getTime());
      return copy;
    }
    
    Number.prototype.clone = 
    Boolean.prototype.clone =
    String.prototype.clone = function() {
      return this;
    }
    

    另见Andy Burke在答案中的注释 .

  • 24

    对克隆简单对象感兴趣:

    JSON.parse(JSON.stringify(json_original));

    资料来源:How to copy JavaScript object to new variable NOT by reference?

  • 740

    为JavaScript中的任何对象执行此操作并不简单或直接 . 您将遇到错误地从对象原型中拾取属性的问题,该属性应保留在原型中而不会复制到新实例 . 例如,如果要将 clone 方法添加到 Object.prototype ,如某些答案所示,则需要显式跳过该属性 . 但是,如果在 Object.prototype 或其他中间原型中添加了其他额外方法,那么您需要使用hasOwnProperty方法检测无法预料的非本地属性 .

    除了不可枚举的属性,当您尝试复制具有隐藏属性的对象时,您将遇到更严峻的问题 . 例如, prototype 是函数的隐藏属性 . 此外,对象的原型使用属性 __proto__ 引用,该属性也是隐藏的,并且不会被遍历源对象属性的for / in循环复制 . 我认为 __proto__ 可能特定于Firefox 's JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don'知道以任何方式自动发现它 .

    寻求优雅解决方案的另一个障碍是正确设置原型继承的问题 . 如果你的源对象的原型是 Object ,那么就这么简单使用 {} 创建一个新的通用对象会起作用,但是如果源的原型是 Object 的某个后代,那么你将会错过使用 hasOwnProperty 过滤器跳过的原型中的其他成员,或原型中的那些,但是没有't enumerable in the first place. One solution might be to call the source object' s constructor 属性来获取初始复制对象,然后复制属性,但是你仍然不会得到不可枚举的属性 . 例如,Date对象将其数据存储为隐藏成员:

    function clone(obj) {
        if (null == obj || "object" != typeof obj) return obj;
        var copy = obj.constructor();
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
        }
        return copy;
    }
    
    var d1 = new Date();
    
    /* Executes function after 5 seconds. */
    setTimeout(function(){
        var d2 = clone(d1);
        alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
    }, 5000);
    

    d1 的日期字符串将落后 d2 的5秒 . 使一个 Date 与另一个相同的方法是调用 setTime 方法,但这是特定于 Date 类的 . 我不认为这个问题有一个防弹的一般解决方案,但我会很高兴出错!

    当我不得不实施一般的深度复制时,我最终通过假设我只需复制一个普通的 ObjectArrayDateStringNumberBoolean 来妥协 . 最后3种类型是不可变的,所以我可以执行浅拷贝而不用担心它会改变 . 我进一步假设 ObjectArray 中包含的任何元素也将是该列表中的6种简单类型之一 . 这可以通过以下代码完成:

    function clone(obj) {
        var copy;
    
        // Handle the 3 simple types, and null or undefined
        if (null == obj || "object" != typeof obj) return obj;
    
        // Handle Date
        if (obj instanceof Date) {
            copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
    
        // Handle Array
        if (obj instanceof Array) {
            copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = clone(obj[i]);
            }
            return copy;
        }
    
        // Handle Object
        if (obj instanceof Object) {
            copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
            }
            return copy;
        }
    
        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    

    只要对象和数组中的数据形成树结构,上述函数就可以适用于我提到的6种简单类型 . 也就是说,对象中的相同数据的引用不超过一个 . 例如:

    // This would be cloneable:
    var tree = {
        "left"  : { "left" : null, "right" : null, "data" : 3 },
        "right" : null,
        "data"  : 8
    };
    
    // This would kind-of work, but you would get 2 copies of the 
    // inner node instead of 2 references to the same copy
    var directedAcylicGraph = {
        "left"  : { "left" : null, "right" : null, "data" : 3 },
        "data"  : 8
    };
    directedAcyclicGraph["right"] = directedAcyclicGraph["left"];
    
    // Cloning this would cause a stack overflow due to infinite recursion:
    var cyclicGraph = {
        "left"  : { "left" : null, "right" : null, "data" : 3 },
        "data"  : 8
    };
    cyclicGraph["right"] = cyclicGraph;
    

    它将无法处理任何JavaScript对象,但它可能足以用于许多目的,只要您不认为它只适用于您抛出的任何内容 .

  • 10

    在ES-6中,您可以简单地使用Object.assign(...) . 例如:

    let obj = {person: 'Thor Odinson'};
    let clone = Object.assign({}, obj);
    

    这里有一个很好的参考:https://googlechrome.github.io/samples/object-assign-es6/

  • 19

    您可以使用一行代码克隆对象并从前一个引用中删除任何引用 . 简单地说:

    var obj1 = { text: 'moo1' };
    var obj2 = Object.create(obj1); // Creates a new clone without references
    
    obj2.text = 'moo2'; // Only updates obj2's text property
    
    console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}
    

    对于当前不支持Object.create的浏览器/引擎,您可以使用此polyfill:

    // Polyfill Object.create if it does not exist
    if (!Object.create) {
        Object.create = function (o) {
            var F = function () {};
            F.prototype = o;
            return new F();
        };
    }
    
  • 17

    这是A. Levy的代码的改编,也用于处理函数和多个/循环引用的克隆 - 这意味着如果克隆的树中的两个属性是同一对象的引用,则克隆的对象树将具有这些属性指向引用对象的同一个克隆 . 这也解决了循环依赖的情况,如果不处理,会导致无限循环 . 算法的复杂性是O(n)

    function clone(obj){
        var clonedObjectsArray = [];
        var originalObjectsArray = []; //used to remove the unique ids when finished
        var next_objid = 0;
    
        function objectId(obj) {
            if (obj == null) return null;
            if (obj.__obj_id == undefined){
                obj.__obj_id = next_objid++;
                originalObjectsArray[obj.__obj_id] = obj;
            }
            return obj.__obj_id;
        }
    
        function cloneRecursive(obj) {
            if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;
    
            // Handle Date
            if (obj instanceof Date) {
                var copy = new Date();
                copy.setTime(obj.getTime());
                return copy;
            }
    
            // Handle Array
            if (obj instanceof Array) {
                var copy = [];
                for (var i = 0; i < obj.length; ++i) {
                    copy[i] = cloneRecursive(obj[i]);
                }
                return copy;
            }
    
            // Handle Object
            if (obj instanceof Object) {
                if (clonedObjectsArray[objectId(obj)] != undefined)
                    return clonedObjectsArray[objectId(obj)];
    
                var copy;
                if (obj instanceof Function)//Handle Function
                    copy = function(){return obj.apply(this, arguments);};
                else
                    copy = {};
    
                clonedObjectsArray[objectId(obj)] = copy;
    
                for (var attr in obj)
                    if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                        copy[attr] = cloneRecursive(obj[attr]);                 
    
                return copy;
            }       
    
    
            throw new Error("Unable to copy obj! Its type isn't supported.");
        }
        var cloneObj = cloneRecursive(obj);
    
    
    
        //remove the unique ids
        for (var i = 0; i < originalObjectsArray.length; i++)
        {
            delete originalObjectsArray[i].__obj_id;
        };
    
        return cloneObj;
    }
    

    一些快速测试

    var auxobj = {
        prop1 : "prop1 aux val", 
        prop2 : ["prop2 item1", "prop2 item2"]
        };
    
    var obj = new Object();
    obj.prop1 = "prop1_value";
    obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
    obj.nr = 3465;
    obj.bool = true;
    
    obj.f1 = function (){
        this.prop1 = "prop1 val changed by f1";
    };
    
    objclone = clone(obj);
    
    //some tests i've made
    console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));
    
    objclone.f1();
    console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
    objclone.f1.prop = 'some prop';
    console.log("test function cloning 2: " + (obj.f1.prop == undefined));
    
    objclone.prop2[0].prop1 = "prop1 aux val NEW";
    console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
    console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
    
  • 1375

    我只是想在这篇文章中添加所有的 Object.create 解决方案,这对于nodejs来说并不是以预期的方式工作 .

    在Firefox中的结果

    var a = {"test":"test"};
    var b = Object.create(a);
    console.log(b);´
    

    {test:"test"} .

    在nodejs中

    {}
    
  • 21

    由于mindeavor声明要克隆的对象是'literal-constructed'对象,因此解决方案可能是简单地多次生成对象而不是克隆对象的实例:

    function createMyObject()
    {
        var myObject =
        {
            ...
        };
        return myObject;
    }
    
    var myObjectInstance1 = createMyObject();
    var myObjectInstance2 = createMyObject();
    
  • 124

    OK, 想象你下面有这个对象,你想克隆它:

    let obj = {a:1, b:2, c:3}; //ES6
    

    要么

    var obj = {a:1, b:2, c:3}; //ES5
    

    答案主要取决于你使用的 ECMAscript ,在 ES6+ 中,你可以简单地使用 Object.assign 来做克隆:

    let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};
    

    或使用像这样的传播运算符:

    let cloned = {...obj}; //new {a:1, b:2, c:3};
    

    但是如果你使用 ES5 ,你可以使用很少的方法,但是 JSON.stringify ,只是确保你没有使用大量的数据来复制,但在许多情况下它可能是一种方便的方式,如下所示:

    let cloned = JSON.parse(JSON.stringify(obj)); 
    //new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over
    
  • 6
    function clone(src, deep) {
    
        var toString = Object.prototype.toString;
        if(!src && typeof src != "object"){
            //any non-object ( Boolean, String, Number ), null, undefined, NaN
            return src;
        }
    
        //Honor native/custom clone methods
        if(src.clone && toString.call(src.clone) == "[object Function]"){
            return src.clone(deep);
        }
    
        //DOM Elements
        if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
            return src.cloneNode(deep);
        }
    
        //Date
        if(toString.call(src) == "[object Date]"){
            return new Date(src.getTime());
        }
    
        //RegExp
        if(toString.call(src) == "[object RegExp]"){
            return new RegExp(src);
        }
    
        //Function
        if(toString.call(src) == "[object Function]"){
            //Wrap in another method to make sure == is not true;
            //Note: Huge performance issue due to closures, comment this :)
            return (function(){
                src.apply(this, arguments);
            });
    
        }
    
        var ret, index;
        //Array
        if(toString.call(src) == "[object Array]"){
            //[].slice(0) would soft clone
            ret = src.slice();
            if(deep){
                index = ret.length;
                while(index--){
                    ret[index] = clone(ret[index], true);
                }
            }
        }
        //Object
        else {
            ret = src.constructor ? new src.constructor() : {};
            for (var prop in src) {
                ret[prop] = deep
                    ? clone(src[prop], true)
                    : src[prop];
            }
        }
    
        return ret;
    };
    

相关问题