如何正确克隆JavaScript对象?

min*_*vor 2922 javascript clone javascript-objects

我有一个对象,x.我想把它复制为对象y,这样改变y就不要修改了x.我意识到复制从内置JavaScript对象派生的对象将导致额外的,不需要的属性.这不是问题,因为我正在复制我自己的一个文字构造的对象.

如何正确克隆JavaScript对象?

A. *_*evy 1495

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

除了不可枚举的属性,当您尝试复制具有隐藏属性的对象时,您将遇到更严峻的问题.例如,prototype是函数的隐藏属性.此外,对象的原型使用属性进行引用,该属性__proto__也是隐藏的,并且不会通过迭代源对象属性的for/in循环进行复制.我认为__proto__可能是Firefox的JavaScript解释器特有的,它可能在其他浏览器中有所不同,但是你可以了解它.并非一切都是可以计算的.如果您知道其名称,则可以复制隐藏属性,但我不知道有任何方法可以自动发现它.

寻求优雅解决方案的另一个障碍是正确设置原型继承的问题.如果您的源对象的原型是Object,那么只需创建一个新的通用对象{}将工作,但如果源的原型是其后代Object,那么您将错过使用hasOwnProperty过滤器跳过的原型中的其他成员,或者在原型中,但首先不是可枚举的.一种解决方案可能是调用源对象的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);
Run Code Online (Sandbox Code Playgroud)

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

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

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

只要对象和数组中的数据形成树结构,上述函数就可以适用于我提到的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;
Run Code Online (Sandbox Code Playgroud)

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

  • 对象分配似乎没有在Chrome中创建真正的副本,维护对原始对象的引用 - 最终使用JSON.stringify和JSON.parse来克隆 - 完美地工作 (8认同)
  • 现在``JSON.parse(JSON.stringify([some object]),[some revirer function])`是一个解决方案吗? (6认同)
  • *几乎*在nodejs中运行良好 - 只需更改(var i = 0,var len = obj.length; i <len; ++ i){to for(var i = 0; i <obj.长度; ++ i){ (5认同)
  • 对于未来的googlers:相同的深层副本,递归传递引用,而不是使用https://gist.github.com/2234277上的'return'语句 (5认同)
  • 在第一个片段中,你确定它不应该是`var cpy = new obj.constructor()`吗? (5认同)
  • @javierfp:我认为它是可以访问的.instanceof运算的工作原理是检查对象原型链(根据Mozilla的javascript参考:https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/instanceof).我想有人可以修改原型链,不再包含Object.这将是不寻常的,但会导致错误抛出. (2认同)
  • 仅仅问一个问题,即如何简单地复制一个对象就需要59页长的可能答案,而不是1秒,1行的确定答案,这一事实表明Javascript和一般软件中的混乱状态。这超出了对草率的最极端的定义,而且具有讽刺意味的是,可以在“语言”的设计中发现这种草率。……想象一下我们的“服务”中的AI实现会发生什么。 (2认同)
  • @JoshuaDavid 的更新,目前 82.57% 的浏览器都支持。 (2认同)
  • 如果我想“克隆”“对象”并且还想“附加”一些东西怎么办? (2认同)

hei*_*nob 915

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

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()
Run Code Online (Sandbox Code Playgroud)

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

另请参阅本文关于在向工作人员发送消息和从工作人员发布消息时使用的浏览器的结构化克隆算法.它还包含深度克隆功能.

  • @Nux,为什么不在时间和记忆方面达到最佳?MiJyn说:"这个方法比浅层复制(在深层对象上)慢的原因是这个方法,根据定义,深度复制.但由于JSON是在本机代码中实现的(在大多数浏览器中),这将大大加快比使用任何其他基于javascript的深度复制解决方案,有时可能比基于javascript的浅层复制技术更快(参见:jsperf.com/cloning-an-object/79).http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-an-object/5344074#5344074 (73认同)
  • 请注意,这只能用于测试.首先,它在时间和内存消耗方面远非最佳.其次,并非所有浏览器都有此方法. (42认同)
  • 我只想在2014年10月为此添加更新.使用JSON.parse(JSON.stringify(oldObject))可以更快地使用Chrome 37+; 使用它的好处是,如果需要,javascript引擎很容易看到并优化为更好的东西. (15认同)
  • **2016更新:**现在应该可以在几乎所有被广泛使用的浏览器中使用.(参见[我可以使用...](http://caniuse.com/#feat=json))现在的主要问题是它是否具有足够的性能. (14认同)
  • 您可以始终包含JSON2.js或JSON3.js.无论如何,你还需要它们.但我确实认为这可能不是最好的解决方案,因为JSON.stringify不包含继承的属性. (2认同)
  • 不适用于稀疏数组`a = []; 一个[5] = 10; JSON.parse(JSON.stringify(一))!==了` (2认同)
  • 可以完成这项工作,但是,这违背了任何良好的编程实践。在巴西,我们称其为“Gambiarra” (2认同)

Pas*_*cal 768

使用jQuery,您可以使用extend进行浅层复制:

var copiedObject = jQuery.extend({}, originalObject)
Run Code Online (Sandbox Code Playgroud)

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

或者进行深层复制:

var copiedObject = jQuery.extend(true, {}, originalObject)
Run Code Online (Sandbox Code Playgroud)

  • 甚至:`var copiedObject = jQuery.extend({},originalObject);` (164认同)
  • 将true指定为深拷贝的第一个参数也很有用:`jQuery.extend(true,{},originalObject);` (82认同)
  • 是的,我发现此链接很有用(与Pascal相同的解决方案)http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object (6认同)
  • 只是注意,这不会复制原始对象的_proto_构造函数 (3认同)

Vit*_*nko 645

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

var x = {myProp: "value"};
var y = Object.assign({}, x); 
Run Code Online (Sandbox Code Playgroud)

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

  • 还要注意,这将复制通过对象文字定义的"方法"(因为这些*是*可枚举的)但是*not*方法通过"类"机制来定义(因为这些*不是*可枚举的). (20认同)
  • 我想应该提到的是,除了Edge之外,IE并没有支持.有些人仍然使用这个. (14认同)

Tar*_*req 204

每个MDN:

  • 如果你想要浅拷贝,请使用 Object.assign({}, a)
  • 对于"深度"复制,请使用 JSON.parse(JSON.stringify(a))

不需要外部库,但您需要首先检查浏览器兼容性.

  • JSON.parse(JSON.stringify(a))看起来很漂亮,但在使用它之前,我建议您对所需的集合进行基准测试.根据对象大小,这可能不是最快的选择. (6认同)
  • 我注意到JSON方法将日期对象转换为字符串但不返回日期.必须在Javascript中处理时区的乐趣并手动修复任何日期.除日期外,其他类型可能类似 (3认同)

itp*_*orn 132

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

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

  1. 这种继承是有用的(呃!)
  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
Run Code Online (Sandbox Code Playgroud)

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

  • 这是原型继承,而不是克隆.这些是完全不同的东西.新对象没有任何属性,只是指向原型的属性.克隆的目的是创建一个新的新对象,它不引用另一个对象中的任何属性. (99认同)
  • 我完全赞成你.我也同意这不是克隆,因为可能是"打算".但是,遇到人,接受JavaScript的本质,而不是试图找到不标准化的模糊解决方案.当然,你不喜欢原型,它们对你来说都是"啰嗦",但如果你知道你在做什么,它们实际上非常有用. (7认同)
  • @RobG:本文解释了引用和克隆之间的区别:http://en.wikipedia.org/wiki/Cloning_ (programming).`Object.create`通过引用指向父项的属性.这意味着如果父项的属性值发生变化,则子项的属性也会发生变化.对于嵌套数组和对象,这会产生一些令人惊讶的副作用,如果您不了解它们,可能会导致代码中难以发现的错误:http://jsbin.com/EKivInO/2/.克隆对象是一个全新的独立对象,它具有与父对象相同的属性和值,但未连接到父对象. (4认同)

Eug*_*rin 121

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

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

var clone = Object.assign({}, obj);
Run Code Online (Sandbox Code Playgroud)

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

阅读更多...

填充工具以支持旧的浏览器:

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

  • 这只会执行一个浅薄的"克隆" (40认同)

Fab*_*oni 83

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

出发情况

我想深入复制一个Javascript Object与它的所有孩子和他们的孩子,等等.但因为我不是那种正常的开发者,我Object正常的 properties,circular structures甚至是nested objects.

所以让我们创建一个circular structurenested object第一个.

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

function Nested(y) {
    this.y = y;
}
Run Code Online (Sandbox Code Playgroud)

让我们把所有东西放在一起Object命名a.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};
Run Code Online (Sandbox Code Playgroud)

接下来,我们要复制a到一个名为变量的变量中b.

var b = a;

b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)

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

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

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

JSON

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

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

b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)

不要浪费太多时间,你会得到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.");
}
Run Code Online (Sandbox Code Playgroud)

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

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)

递归并circular structures不能很好地一起工作......RangeError: Maximum call stack size exceeded

原生解决方案

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

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)

这个解决方案前一段时间被添加到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"
    }
}
Run Code Online (Sandbox Code Playgroud)

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

用于原生溶液的polyfill

Object.create就像IE 8一样,在旧版浏览器中有一个polyfill .它类似于Mozilla推荐的东西,当然,它并不完美,导致与原生解决方案相同的问题.

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)

我已经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
Run Code Online (Sandbox Code Playgroud)

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

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

在挖掘时,我发现了一个类似的问题(在Javascript中,当执行深层复制时,如何避免循环,由于属性是"这个"?)到这个,但有一个更好的解决方案.

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

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

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

要求相匹配,但仍有一些小问题,包括不断变化instancenestedcircObject.

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

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]
Run Code Online (Sandbox Code Playgroud)

结论

使用递归和缓存的最后一个解决方案可能不是最好的,但它是对象的真正深层副本.它操作简单properties,circular structuresnested object,但它会弄乱他们的实例,同时克隆.

的jsfiddle

  • 所以这个问题就是避免这个问题:) (10认同)
  • 对上面提供的解决方案进行了正确的分析,但作者得出的结论表明这个问题没有解决方案. (2认同)
  • 令人遗憾的是JS不包含本机克隆功能. (2认同)
  • 最好的答案在这里!恭喜您,先生! (2认同)

dul*_*ule 76

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

y = _.clone(x);
Run Code Online (Sandbox Code Playgroud)

或者你可以扩展它

copiedObject = _.extend({},originalObject);
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.在Meteor服务器上使用此技术. (2认同)

Ali*_*eza 55

好吧,想象一下你有这个对象,你想要克隆它:

let obj = {a:1, b:2, c:3}; //ES6
Run Code Online (Sandbox Code Playgroud)

要么

var obj = {a:1, b:2, c:3}; //ES5
Run Code Online (Sandbox Code Playgroud)

答案主要取决于你使用哪个ECMAscriptES6+,你可以简单地Object.assign用来做克隆:

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

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

let cloned = {...obj}; //new {a:1, b:2, c:3};
Run Code Online (Sandbox Code Playgroud)

但是如果你使用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
Run Code Online (Sandbox Code Playgroud)

  • `Object.assign`进行浅拷贝(就像传播,@ Alizera) (2认同)

Moh*_*fei 49

2020 年 7 月 6 日更新

在 JavaScript 中有三 (3) 种方法可以克隆对象。由于 JavaScript 中的对象是引用值,您不能简单地使用 = 进行复制。

方法是:

const food = { food: 'apple', drink: 'milk' }


// 1. Using the "Spread"
// ------------------

{ ...food }


// 2. Using "Object.assign"
// ------------------

Object.assign({}, food)


// 3. "JSON"
// ------------------

JSON.parse(JSON.stringify(food))

// RESULT:
// { food: 'apple', drink: 'milk' }

Run Code Online (Sandbox Code Playgroud)

希望可以作为参考总结。

  • “JSON”方法将删除对象的任何方法 (9认同)
  • 从一个对象创建一个字符串,然后将该字符串解析到另一个对象中只是为了复制该对象,这是 Monty Python 的一种编程风格:-D (3认同)
  • 对于具有层次结构的对象,展开运算符和 Object.assign 会失败,即。嵌套对象。JSON.parse/stringify 可以工作,但如上所述,不会复制方法。 (3认同)

Kri*_*ker 40

一个特别不优雅的解决方案是使用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
Run Code Online (Sandbox Code Playgroud)

  • 函数不是JSON规范的一部分,因为它们不是传输数据的安全(或智能)方式,这就是JSON的用途.我知道Firefox中的原生JSON编码器只是忽略传递给它的函数,但我不确定其他人的行为. (5认同)

mus*_*ind 34

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


完整克隆:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'
Run Code Online (Sandbox Code Playgroud)

克隆二级引用:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这是有效的,但请记住这是一个SHALLOW副本,因此对其他对象的任何深度引用仍然是引用! (3认同)

Luk*_*nek 25

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

var destination = angular.copy(source);
Run Code Online (Sandbox Code Playgroud)

要么

angular.copy(source, destination);
Run Code Online (Sandbox Code Playgroud)

更多angular.copy 文档 ......

  • 这是FYI的深层副本。 (2认同)

Jan*_*roň 22

A.Levy的答案几乎完成,这是我的小贡献:有一种方法如何处理递归引用,看到这一行

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

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

  • `Date.prototype.clone = function(){return new Date(+ this)};` (3认同)

Kam*_*ski 22

表现

今天 2020.04.30 我在 MacOs High Sierra v10.13.6 上的 Chrome v81.0、Safari v13.1 和 Firefox v75.0 上对选定的解决方案进行了测试。

我专注于复制数据的速度(具有简单类型字段的对象,而不是方法等)。解决方案AI只能做浅拷贝,解决方案JU可以做深拷贝。

浅拷贝的结果

  • 解决方案{...obj}(A) 在 chrome 和 firefox 上速度最快,在 safari 上速度中等
  • 基于Object.assign(B) 的解决方案在所有浏览器上都很快
  • jQuery (E) 和 lodash (F,G,H) 解决方案中等/相当快
  • 解决方案JSON.parse/stringify(K)很慢
  • 解决方案 D 和 U 在所有浏览器上都很慢

在此处输入图片说明

深拷贝的结果

  • 解决方案 Q 在所有浏览器上都是最快的
  • jQuery (L) 和 lodash (J) 中等速度
  • 解决方案JSON.parse/stringify(K)很慢
  • 解决方案 U 在所有浏览器上最慢
  • lodash (J) 和解决方案 U 在 Chrome 上崩溃 1000 级深度对象

在此处输入图片说明

细节

对于选择的解决方案: A B C(my) D E F G H I J K L M N O P Q R S T U,我进行了 4 次测试

  • 浅小:具有 10 个非嵌套字段的对象 - 您可以在此处运行它
  • 浅大:具有 1000 个非嵌套字段的对象 - 您可以在此处运行它
  • deep-small:具有 10 级嵌套字段的对象 - 您可以在此处运行它
  • deep-big:具有 1000 个级别嵌套字段的对象 - 您可以在此处运行它

测试中使用的对象显示在下面的片段中

let obj_ShallowSmall = {
  field0: false,
  field1: true,
  field2: 1,
  field3: 0,
  field4: null,
  field5: [],
  field6: {},
  field7: "text7",
  field8: "text8",
}

let obj_DeepSmall = {
  level0: {
   level1: {
    level2: {
     level3: {
      level4: {
       level5: {
        level6: {
         level7: {
          level8: {
           level9: [[[[[[[[[['abc']]]]]]]]]],
  }}}}}}}}},
};

let obj_ShallowBig = Array(1000).fill(0).reduce((a,c,i) => (a['field'+i]=getField(i),a) ,{});


let obj_DeepBig = genDeepObject(1000);



// ------------------
// Show objects
// ------------------

console.log('obj_ShallowSmall:',JSON.stringify(obj_ShallowSmall));
console.log('obj_DeepSmall:',JSON.stringify(obj_DeepSmall));
console.log('obj_ShallowBig:',JSON.stringify(obj_ShallowBig));
console.log('obj_DeepBig:',JSON.stringify(obj_DeepBig));




// ------------------
// HELPERS
// ------------------

function getField(k) {
  let i=k%10;
  if(i==0) return false;
  if(i==1) return true;
  if(i==2) return k;
  if(i==3) return 0;
  if(i==4) return null;
  if(i==5) return [];
  if(i==6) return {};  
  if(i>=7) return "text"+k;
}

function genDeepObject(N) {
  // generate: {level0:{level1:{...levelN: {end:[[[...N-times...['abc']...]]] }}}...}}}
  let obj={};
  let o=obj;
  let arr = [];
  let a=arr;

  for(let i=0; i<N; i++) {
    o['level'+i]={};
    o=o['level'+i];
    let aa=[];
    a.push(aa);
    a=aa;
  }

  a[0]='abc';
  o['end']=arr;
  return obj;
}
Run Code Online (Sandbox Code Playgroud)

下面的片段展示了经过测试的解决方案并显示了它们之间的差异

function A(obj) {
  return {...obj}
}

function B(obj) {
  return Object.assign({}, obj); 
}

function C(obj) {
  return Object.keys(obj).reduce( (a,c) => (a[c]=obj[c], a), {})
}

function D(obj) {
  let copyOfObject = {};
  Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(obj));
  return copyOfObject;
}

function E(obj) {
  return jQuery.extend({}, obj) // shallow
}

function F(obj) {
  return _.clone(obj);
}

function G(obj) {
  return _.clone(obj,true);
}

function H(obj) {
  return _.extend({},obj);
}

function I(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;
}

function J(obj) {
  return _.cloneDeep(obj,true);
}

function K(obj) {
	return JSON.parse(JSON.stringify(obj));
}

function L(obj) {
  return jQuery.extend(true, {}, obj) // deep
}

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

function N(obj) {
  let EClone = function(obj) {
    var newObj = (obj instanceof Array) ? [] : {};
    for (var i in obj) {
      if (i == 'EClone') continue;
      if (obj[i] && typeof obj[i] == "object") {
        newObj[i] = EClone(obj[i]);
      } else newObj[i] = obj[i]
    } return newObj;
  };

	return EClone(obj);
};

function O(obj) {
    if (obj == null || typeof obj != "object") return obj;
    if (obj.constructor != Object && obj.constructor != Array) return obj;
    if (obj.constructor == Date || obj.constructor == RegExp || obj.constructor == Function ||
        obj.constructor == String || obj.constructor == Number || obj.constructor == Boolean)
        return new obj.constructor(obj);

    let to = new obj.constructor();

    for (var name in obj)
    {
        to[name] = typeof to[name] == "undefined" ? O(obj[name], null) : to[name];
    }

    return to;
}

function P(obj) {
  function clone(target, source){

      for(let key in source){

          // Use getOwnPropertyDescriptor instead of source[key] to prevent from trigering setter/getter.
          let descriptor = Object.getOwnPropertyDescriptor(source, key);
          if(descriptor.value instanceof String){
              target[key] = new String(descriptor.value);
          }
          else if(descriptor.value instanceof Array){
              target[key] = clone([], descriptor.value);
          }
          else if(descriptor.value instanceof Object){
              let prototype = Reflect.getPrototypeOf(descriptor.value);
              let cloneObject = clone({}, descriptor.value);
              Reflect.setPrototypeOf(cloneObject, prototype);
              target[key] = cloneObject;
          }
          else {
              Object.defineProperty(target, key, descriptor);
          }
      }
      let prototype = Reflect.getPrototypeOf(source);
      Reflect.setPrototypeOf(target, prototype);
      return target;
  }
  return clone({},obj);
}

function Q(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] = Q(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = Q(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

function R(obj) {
    const gdcc = "__getDeepCircularCopy__";
    if (obj !== Object(obj)) {
        return obj; // primitive value
    }

    var set = gdcc in obj,
        cache = obj[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    obj[gdcc] = function() { return result; }; // overwrite
    if (obj instanceof Array) {
        result = [];
        for (var i=0; i<obj.length; i++) {
            result[i] = R(obj[i]);
        }
    } else {
        result = {};
        for (var prop in obj)
            if (prop != gdcc)
                result[prop] = R(obj[prop]);
            else if (set)
                result[prop] = R(cache);
    }
    if (set) {
        obj[gdcc] = cache; // reset
    } else {
        delete obj[gdcc]; // unset again
    }
    return result;
}

function S(obj) {
    const cache = new WeakMap(); // Map of old - new references

    function copy(object) {
        if (typeof object !== 'object' ||
            object === null ||
            object instanceof HTMLElement
        )
            return object; // primitive value or HTMLElement

        if (object instanceof Date) 
            return new Date().setTime(object.getTime());

        if (object instanceof RegExp) 
            return new RegExp(object.source, object.flags);

        if (cache.has(object)) 
            return cache.get(object);

        const result = object instanceof Array ? [] : {};

        cache.set(object, result); // store reference to object before the recursive starts

        if (object instanceof Array) {
            for(const o of object) {
                 result.push(copy(o));
            }
            return result;
        }

        const keys = Object.keys(object); 

        for (const key of keys)
            result[key] = copy(object[key]);

        return result;
    }

    return copy(obj);
}

function T(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;
}

function U(obj) {
  /*
    Deep copy objects by value rather than by reference,
    exception: `Proxy`
  */

  const seen = new WeakMap()

  return clone(obj)

  function defineProp(object, key, descriptor = {}, copyFrom = {}) {
    const { configurable: _configurable, writable: _writable }
      = Object.getOwnPropertyDescriptor(object, key)
      || { configurable: true, writable: true }

    const test = _configurable // Can redefine property
      && (_writable === undefined || _writable) // Can assign to property

    if (!test || arguments.length <= 2) return test

    const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
      || { configurable: true, writable: true } // Custom…
      || {}; // …or left to native default settings

    ["get", "set", "value", "writable", "enumerable", "configurable"]
      .forEach(attr =>
        descriptor[attr] === undefined &&
        (descriptor[attr] = basisDesc[attr])
      )

    const { get, set, value, writable, enumerable, configurable }
      = descriptor

    return Object.defineProperty(object, key, {
      enumerable, configurable, ...get || set
        ? { get, set } // Accessor descriptor
        : { value, writable } // Data descriptor
    })
  }

  function clone(object) {
    if (object !== Object(object)) return object /*
    —— Check if the object belongs to a primitive data type */

    if (object instanceof Node) return object.cloneNode(true) /*
    —— Clone DOM trees */

    let _object // The clone of object

    switch (object.constructor) {
      case Array:
      case Object:
        _object = cloneObject(object)
        break

      case Date:
        _object = new Date(+object)
        break

      case Function:
        const fnStr = String(object)

        _object = new Function("return " +
          (/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr)
            ? "function " : ""
          ) + fnStr
        )()

        copyPropDescs(_object, object)
        break

      case RegExp:
        _object = new RegExp(object)
        break

      default:
        switch (Object.prototype.toString.call(object.constructor)) {
          //                              // Stem from:
          case "[object Function]":       // `class`
          case "[object Undefined]":      // `Object.create(null)`
            _object = cloneObject(object)
            break

          default:                        // `Proxy`
            _object = object
        }
    }

    return _object
  }


  function cloneObject(object) {
    if (seen.has(object)) return seen.get(object) /*
    —— Handle recursive references (circular structures) */

    const _object = Array.isArray(object)
      ? []
      : Object.create(Object.getPrototypeOf(object)) /*
        —— Assign [[Prototype]] for inheritance */

    seen.set(object, _object) /*
    —— Make `_object` the associative mirror of `object` */

    Reflect.ownKeys(object).forEach(key =>
      defineProp(_object, key, { value: clone(object[key]) }, object)
    )

    return _object
  }


  function copyPropDescs(target, source) {
    Object.defineProperties(target,
      Object.getOwnPropertyDescriptors(source)
    )
  }
}
 
// ------------------------
// Test properties
// ------------------------


console.log(`  shallow deep  func  circ  undefined date  RegExp bigInt`)

log(A);
log(B);
log(C);
log(D);
log(E);
log(F);
log(G);
log(H);
log(I);
log(J);
log(K);
log(L);
log(M);
log(N);
log(O);
log(P);
log(Q);
log(R);
log(S);
log(T);
log(U);

console.log(`  shallow deep  func  circ  undefined date  RegExp bigInt
----
LEGEND:
shallow - solution create shallow copy
deep - solution create deep copy
func - solution copy functions
circ - solution can copy object with circular references
undefined - solution copy fields with undefined value
date - solution can copy date
RegExp - solution can copy fields with regular expressions
bigInt - solution can copy BigInt
`)


// ------------------------
// Helper functions
// ------------------------


function deepCompare(obj1,obj2) {
  return JSON.stringify(obj1)===JSON.stringify(obj2);
}

function getCase() { // pure data case
  return { 
    undef: undefined,
    bool: true, num: 1, str: "txt1",    
    e1: null, e2: [], e3: {}, e4: 0, e5: false,
    arr: [ false, 2, "txt3", null, [], {},
      [ true,4,"txt5",null, [], {},  [true,6,"txt7",null,[],{} ], 
        {bool: true,num: 8, str: "txt9", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
      ],
        {bool: true,num: 10, str: "txt11", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
    ], 
    obj: { 
        bool: true, num: 12, str: "txt13",
        e1: null, e2: [], e3: {}, e4: 0, e5: false,
        arr: [true,14,"txt15",null,[],{} ],
        obj: { 
          bool: true, num: 16, str: "txt17",
          e1: null, e2: [], e3: {}, e4: 0, e5: false,
          arr: [true,18,"txt19",null,[],{} ],
          obj: {bool: true,num: 20, str: "txt21", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
      } 
    } 
  };
}

function check(org, copy, field, newValue) {
  copy[field] = newValue;
  return deepCompare(org,copy); 
}

function testFunc(f) {
	let o = { a:1, fun: (i,j)=> i+j };
  let c = f(o);
  
  let val = false
  try{
    val = c.fun(3,4)==7;
  } catch(e) { }
  return val;
} 

function testCirc(f) {
	function Circ() {
    this.me = this;
  }

  var o = {
      x: 'a',
      circ: new Circ(),
      obj_circ: null,
  };
  
  o.obj_circ = o;

  let val = false;

  try{
    let c = f(o);  
    val = (o.obj_circ == o) && (o.circ == o.circ.me);
  } catch(e) { }
  return val;
} 

function testRegExp(f) {
  let o = {
    re: /a[0-9]+/,
  };
  
  let val = false;

  try{
    let c = f(o);  
    val = (String(c.re) == String(/a[0-9]+/));
  } catch(e) { }
  return val;
}

function testDate(f) {
  let o = {
    date: new Date(),
  };
  
  let val = false;

  try{
    let c = f(o);  
    val = (+new Date(c.date) == +new Date(o.date));
  } catch(e) { }
  return val;
}

function testBigInt(f) {
  let val = false;
  
  try{
    let o = {
      big: 123n,
    };
  
    let c = f(o);  
  
    val = o.big == c.big;
  } catch(e) { }
  
  return val;
}

function log(f) {
  let o = getCase();  // orginal object
  let oB = getCase(); // "backup" used for shallow valid test
  
  let c1 = f(o); // copy 1 for reference
  let c2 = f(o); // copy 2 for test shallow values
  let c3 = f(o); // copy 3 for test deep values

  let is_proper_copy = deepCompare(c1,o);  // shoud be true
  
  // shallow changes
  let testShallow = 
    [ ['bool',false],['num',666],['str','xyz'],['arr',[]],['obj',{}] ]
    .reduce((acc,curr)=> acc && check(c1,c2,curr[0], curr[1]), true );
  
  // should be true (original object shoud not have changed shallow fields)
  let is_valid = deepCompare(o,oB); 

  // deep test (intruduce some change)
  if (c3.arr[6]) c3.arr[6][7].num = 777;
  
  let diff_shallow = !testShallow; // shoud be true (shallow field was copied)
  let diff_deep = !deepCompare(c1,c3);    // shoud be true (deep field was copied)
  let can_copy_functions = testFunc(f);
  let can_copy_circular = testCirc(f);
  let can_copy_regexp = testRegExp(f);
  let can_copy_date = testDate(f);
  let can_copy_bigInt = testBigInt(f);
  
  let has_undefined = 'undef' in c1; // field with undefined value is copied?  
  le


Cal*_*vin 19

从这篇文章:如何在Javascript中复制数组和对象 Brian Huisman:

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

  • 这很接近,但不适用于任何对象.尝试使用此方法克隆Date对象.并非所有属性都是可枚举的,因此它们不会全部显示在for/in循环中. (4认同)
  • @ iPadDeveloper2011上面的代码中有一个错误,它创建了一个名为"i"的全局变量(对于i),而不是"(对于此中的var i)".我有足够的业力来编辑和修复它,所以我做了. (3认同)
  • @Calvin:这应该被创建为一个不可枚举的属性,否则 'clone' 将出现在 'for' 循环中。 (2认同)
  • 为什么不是`var copiedObj = Object.create(obj);`这也是一个好方法? (2认同)

pic*_*rdo 19

这是您可以使用的功能.

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

  • 这个答案非常接近,但不太正确.如果您尝试克隆Date对象,则不会获得相同的日期,因为对Date构造函数的调用使用当前日期/时间初始化新Date.该值不可枚举,也不会被for/in循环复制. (10认同)

Joã*_*ira 18

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

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);
Run Code Online (Sandbox Code Playgroud)

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

  • 它没有深度克隆对象. (12认同)

Pav*_*rre 18

在ECMAScript 2018中

let objClone = { ...obj };
Run Code Online (Sandbox Code Playgroud)

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

  • 感谢您提示嵌套对象仍被复制作为参考!在调试代码时我几乎要发疯了,因为我修改了“克隆”上的嵌套属性,但原始属性被修改了。 (2认同)
  • @SunilGarg 要复制嵌套属性,您可以使用 `const objDeepClone = JSON.parse(JSON.stringify(obj));` (2认同)

Rob*_*ans 13

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

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

对于当前不支持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();
    };
}
Run Code Online (Sandbox Code Playgroud)

  • 这是用obj1创建obj2的原型.它只能用于遮蔽obj2中的`text`成员.您没有制作副本,只是在obj2上找不到成员时推迟到原型链. (11认同)
  • 这不会“没有引用”创建它,只是将引用移至原型。仍然是参考。如果原始属性发生变化,则“克隆”中的原型属性也会发生变化。根本不是克隆。 (2认同)

小智 13

对克隆简单对象感兴趣:

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

来源:如何通过引用将JavaScript对象复制到新变量?


Cha*_*iam 13

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

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}
Run Code Online (Sandbox Code Playgroud)

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

JavaScript不断发展.

  • 当您在对象上定义了函数时,它不起作用 (2认同)

VaZ*_*ZaA 11

使用Lodash:

var y = _.clone(x, true);
Run Code Online (Sandbox Code Playgroud)

  • 我更喜欢`_.cloneDeep(x)`,因为它基本上和上面一样,但读得更好. (5认同)
  • OMG重新克隆克隆将是疯狂的.这是唯一合理的答案. (4认同)

flo*_*ori 11

let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)
Run Code Online (Sandbox Code Playgroud)

ES6解决方案,如果你想(浅)克隆一个类实例而不只是一个属性对象.


Con*_*ver 9

我认为有一个简单而有效的答案.在深度复制中有两个问题:

  1. 保持属性彼此依赖.
  2. 并使方法在克隆对象上保持活跃.

所以我认为一个简单的解决方案是首先序列化和反序列化,然后对其进行分配以复制函数.

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);
Run Code Online (Sandbox Code Playgroud)

虽然这个问题有很多答案,但我希望这个问题也有帮助.

  • 我正在使用JSON.parse(JSON.stringify(source))。一直在工作。 (2认同)
  • @Misha,这样您将错过所有功能。术语“作品”具有许多含义。 (2认同)

小智 8

对于深层复制和克隆,先JSON.stringify然后再JSON.parse对象:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
Run Code Online (Sandbox Code Playgroud)


Pul*_*hri 8

我在复制对象时遇到问题。这是因为当您执行以下操作时,您只是对对象进行“引用”,并且当稍后更新源对象值时,克隆的复制对象也会更改值,因为它只是一个“引用”,因此您查看源对象最后更改的多个值。

let x = { a: 1 };
let y = x; // y is a reference to x, so if x changes y also changes and v/v
Run Code Online (Sandbox Code Playgroud)

因此,要解决此问题,您需要执行以下操作:

let y = JSON.parse(JSON.stringify(x)); //see Note below
Run Code Online (Sandbox Code Playgroud)

防止引用的另一种方法是执行以下操作:

let x = { a: 1 };
let y = Object.assign({}, x); // Object.assign(target, ...sources)

y.a = 2;
console.log(x); // { a: 1 }
console.log(y); // { a: 2 }
Run Code Online (Sandbox Code Playgroud)

注意: https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#warning_for_deep_clone


rel*_*89n 7

最正确的复制对象是使用Object.create

Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
Run Code Online (Sandbox Code Playgroud)

这样的符号将使相同的对象具有正确的原型和隐藏属性。


hei*_*nob 6

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

在Firefox中的结果

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´
Run Code Online (Sandbox Code Playgroud)

{test:"test"}.

在nodejs中

{}
Run Code Online (Sandbox Code Playgroud)


Rad*_*scu 6

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

一些快速测试

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

  • 截至 2016 年 9 月,这是该问题*唯一*正确的解决方案。 (2认同)

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

  • `if(!src && typeof src!="object"){`.我认为应该是`||`而不是`&&`. (2认同)

Ber*_*ink 6

由于mindeavor声明要克隆的对象是"文字构造的"对象,因此解决方案可能是简单地多次生成对象而不是克隆对象的实例:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
Run Code Online (Sandbox Code Playgroud)


小智 6

结构化克隆

HTML 标准包括一个内部结构化克隆/序列化算法,可以创建对象的深层克隆。它仍然仅限于某些内置类型,但除了 JSON 支持的少数类型之外,它还支持日期、正则表达式、地图、集合、Blob、文件列表、图像数据、稀疏数组、类型化数组,以及将来可能更多. 它还保留克隆数据中的引用,允许它支持会导致 JSON 错误的循环和递归结构。

Node.js 中的支持:实验性

v8Node.js 中的模块当前(从 Node 11 开始)直接公开结构化序列化 API,但此功能仍标记为“实验性”,并且在未来版本中可能会更改或删除。如果您使用的是兼容版本,则克隆对象非常简单:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};
Run Code Online (Sandbox Code Playgroud)

浏览器的直接支持:也许最终?

浏览器目前没有为结构化克隆算法提供直接接口,但在 GitHub 上的 whatwg/html#793 中structuredClone()已经讨论了一个全局函数。按照目前的提议,将其用于大多数目的将非常简单:

const clone = structuredClone(original);
Run Code Online (Sandbox Code Playgroud)

除非发布,否则浏览器的结构化克隆实现只会间接暴露。

异步解决方法:可用。

使用现有 API 创建结构化克隆的开销较低的方法是通过MessageChannels 的一个端口发布数据。另一个端口将发出一个message带有附加.data. 不幸的是,监听这些事件必然是异步的,而同步的替代方案不太实用。

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);
Run Code Online (Sandbox Code Playgroud)

使用示例:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();
Run Code Online (Sandbox Code Playgroud)

同步解决方法:太糟糕了!

同步创建结构化克隆没有好的选择。这里有一些不切实际的技巧。

history.pushState()并且history.replaceState()都创建了第一个参数的结构化克隆,并将该值分配给history.state. 您可以使用它来创建任何对象的结构化克隆,如下所示:

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};
Run Code Online (Sandbox Code Playgroud)

使用示例:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();
Run Code Online (Sandbox Code Playgroud)

虽然是同步的,但这可能会非常慢。它会产生与操纵浏览器历史记录相关的所有开销。重复调用此方法可能会导致 Chrome 暂时无响应。

Notification构造函数创建其相关数据的结构化克隆。它还尝试向用户显示浏览器通知,但除非您请求通知权限,否则这将静默失败。如果您有其他用途的许可,我们将立即关闭我们创建的通知。

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};
Run Code Online (Sandbox Code Playgroud)

使用示例:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();
Run Code Online (Sandbox Code Playgroud)

  • +1 表示它最终可能以何种形式成为内置的想法——即使现在无法使用。 (2认同)

小智 6

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

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

以下是实施:

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


chi*_*ens 6

原生JS:

const shallowClone = {...originalObj};
const deepClone = JSON.parse(JSON.stringify(originalObj));
Run Code Online (Sandbox Code Playgroud)

使用库:

// Lodash
const shallowClone = _.clone(originalObj);
const deepClone = _. cloneDeep(originalObj);

// JQuery
const shallowClone = jQuery.extend({}, originalObj);
const deepClone = jQuery.extend(true, {}, originalObj);

// Angular
const deepClone = angular.copy(originalObj);
Run Code Online (Sandbox Code Playgroud)


小智 5

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

for ( var i in someArray ) { ... }
Run Code Online (Sandbox Code Playgroud)

迭代遍历数组的元素后,将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; } } );
Run Code Online (Sandbox Code Playgroud)

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


use*_*643 5

deepcopy从使用npm。在浏览器和node作为npm module...

https://www.npmjs.com/package/deepcopy

let a = deepcopy(b)


ooo*_*ooo 5

(以下是主要@的整合马切伊·布考斯基,@ A.征,@ 扬TURO?,@ 热度的答案,@ LeviRoberts,@ RobG的意见,非常感谢他们!)

深复制?—是的!(大多);
浅拷贝?—不!(除外Proxy)。

我真诚欢迎大家参加测试clone()
另外,defineProp()旨在轻松,快速地(重新)定义或复制任何类型的描述符。

功能

"use strict"
function clone(object) {
  /*
    Deep copy objects by value rather than by reference,
    exception: `Proxy`
  */

  const seen = new WeakMap()

  return clone(object)


  function clone(object) {
    if (object !== Object(object)) return object /*
    —— Check if the object belongs to a primitive data type */

    if (object instanceof Node) return object.cloneNode(true) /*
    —— Clone DOM trees */

    let _object // The clone of object

    switch (object.constructor) {
      case Array:
      case Object:
        _object = cloneObject(object)
        break

      case Date:
        _object = new Date(+object)
        break

      case Function:
        const fnStr = String(object)

        _object = new Function("return " +
          (/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr)
            ? "function " : ""
          ) + fnStr
        )()

        copyPropDescs(_object, object)
        break

      case RegExp:
        _object = new RegExp(object)
        break

      default:
        switch (Object.prototype.toString.call(object.constructor)) {
          //                              // Stem from:
          case "[object Function]":       // `class`
          case "[object Undefined]":      // `Object.create(null)`
            _object = cloneObject(object)
            break

          default:                        // `Proxy`
            _object = object
        }
    }

    return _object
  }


  function cloneObject(object) {
    if (seen.has(object)) return seen.get(object) /*
    —— Handle recursive references (circular structures) */

    const _object = Array.isArray(object)
      ? []
      : Object.create(Object.getPrototypeOf(object)) /*
        —— Assign [[Prototype]] for inheritance */

    seen.set(object, _object) /*
    —— Make `_object` the associative mirror of `object` */

    Reflect.ownKeys(object).forEach(key =>
      defineProp(_object, key, { value: clone(object[key]) }, object)
    )

    return _object
  }


  function copyPropDescs(target, source) {
    Object.defineProperties(target,
      Object.getOwnPropertyDescriptors(source)
    )
  }
}


function defineProp(object, key, descriptor = {}, copyFrom = {}) {
  const { configurable: _configurable, writable: _writable }
    = Object.getOwnPropertyDescriptor(object, key)
    || { configurable: true, writable: true }

  const test = _configurable // Can redefine property
    && (_writable === undefined || _writable) // Can assign to property

  if (!test || arguments.length <= 2) return test

  const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
    || { configurable: true, writable: true } // Custom…
    || {}; // …or left to native default settings

  ["get", "set", "value", "writable", "enumerable", "configurable"]
    .forEach(attr =>
      descriptor[attr] === undefined &&
      (descriptor[attr] = basisDesc[attr])
    )

  const { get, set, value, writable, enumerable, configurable }
    = descriptor

  return Object.defineProperty(object, key, {
    enumerable, configurable, ...get || set
      ? { get, set } // Accessor descriptor
      : { value, writable } // Data descriptor
  })
}
Run Code Online (Sandbox Code Playgroud)

//测试

"use strict"
const obj0 = {
  u: undefined,
  nul: null,
  t: true,
  num: 9,
  str: "",
  sym: Symbol("symbol"),
  [Symbol("e")]: Math.E,
  arr: [[0], [1, 2]],
  d: new Date(),
  re: /f/g,
  get g() { return 0 },
  o: {
    n: 0,
    o: { f: function (...args) { } }
  },
  f: {
    getAccessorStr(object) {
      return []
        .concat(...
          Object.values(Object.getOwnPropertyDescriptors(object))
            .filter(desc => desc.writable === undefined)
            .map(desc => Object.values(desc))
        )
        .filter(prop => typeof prop === "function")
        .map(String)
    },
    f0: function f0() { },
    f1: function () { },
    f2: a => a / (a + 1),
    f3: () => 0,
    f4(params) { return param => param + params },
    f5: (a, b) => ({ c = 0 } = {}) => a + b + c
  }
}

defineProp(obj0, "s", { set(v) { this._s = v } })
defineProp(obj0.arr, "tint", { value: { is: "non-enumerable" } })
obj0.arr[0].name = "nested array"


let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", { set(v) { this._s = v + 1 } })
defineProp(obj1.re, "multiline", { value: true })

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Routinely")

console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()

console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()

console.log("obj0\n ",
  ".arr.tint:", obj0.arr.tint, "\n ",
  ".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
  ".arr.tint:", obj1.arr.tint, "\n ",
  ".arr[0].name:", obj1.arr[0].name
)
console.log()

console.log("Accessor-type descriptor\n ",
  "of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
  "of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
  "set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
  "  ? (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)

console.log("—— obj0 has not been interfered.")

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Circular structures")

obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr

obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)

console.log("Clear obj0's recursion:",
  obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
  "obj0\n ",
  ".o.r:", obj0.o.r, "\n ",
  ".arr:", obj0.arr
)
console.log(
  "obj1\n ",
  ".o.r:", obj1.o.r, "\n ",
  ".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")


console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Classes")

class Person {
  constructor(name) {
    this.name = name
  }
}

class Boy extends Person { }
Boy.prototype.sex = "M"

const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }

const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"

boy0.name = "one"
boy1.name = "neo"

console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
  Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)
Run Code Online (Sandbox Code Playgroud)

参考文献

  1. Object.create()| MDN
  2. Object.defineProperties()| MDN
  3. 属性的可枚举性和所有权| MDN
  4. TypeError:循环对象值| MDN

语言技巧

  1. 有条件地向对象添加道具


Ash*_*k R 5

使用lodash _.cloneDeep()。

浅表复制:lodash _.clone()

可以通过简单地复制参考来进行浅表复制。

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}
Run Code Online (Sandbox Code Playgroud)

浅表复制:lodash _.clone()

深度复制:lodash _.cloneDeep()

字段被取消引用:而不是引用要复制的对象

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}
Run Code Online (Sandbox Code Playgroud)

深度复制:lodash _.cloneDeep()


GAN*_*ARE 5

对象复制使用 ( ... )

//bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2,c: 3 }

//good
const originalObj = { id: 5, name: 'San Francisco'};
const copyObject = {...originalObj, pincode: 4444};
console.log(copyObject)  //{ id: 5, name: 'San Francisco', pincode: 4444 }
Run Code Online (Sandbox Code Playgroud)

同样可用于将数组从一个复制到另一个

const itemsCopy = [...items];
Run Code Online (Sandbox Code Playgroud)


小智 5

克隆对象的简单递归方法。也可以使用 lodash.clone。

let clone = (obj) => {
	let obj2 = Array.isArray(obj) ? [] : {};
	for(let k in obj) {
          obj2[k] = (typeof obj[k] === 'object' ) ? clone(obj[k]) :  obj[k];
        }
        return obj2;
    }

let w = { name: "Apple", types: ["Fuji", "Gala"]};
let x = clone(w);
w.name = "Orange";
w.types = ["Navel"];
console.log(x);
console.log(w);
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

1756071 次

最近记录:

5 年,11 月 前

Copyright Info

© Copyright 2013-2021 admin@qa.1r1g.com

如未特别说明,本网站的内容使用如下协议:
Creative Commons Atution-NonCommercial-ShareAlike 4.0 International license
.

用以下方式浏览
回到顶部