JavaScript中最有效的深度克隆对象的方法是什么?

5167
什么是克隆JavaScript对象最有效的方法?我见过使用obj = eval(uneval(o));,但这是非标准的,只有Firefox支持

我已经尝试过像obj = JSON.parse(JSON.stringify(o));这样的方式,但质疑其效率。

我也看到了递归复制函数的各种缺陷。
我很惊讶没有一个权威的解决方案存在。

566
Eval本身并不可怕,使用不当才是。如果你害怕它的副作用,那么你就没有正确地使用它。你所担心的副作用正是使用Eval的原因。顺便问一句,有人真正回答了你的问题吗? - Tegra Detra
15
复制对象是一个棘手的问题,特别是对于任意集合的自定义对象而言。这可能就是为什么没有现成的方法来完成它的原因。 - b01
12
使用 eval() 通常是不明智的,因为许多JavaScript引擎的优化器必须在处理通过 eval() 设置的变量时关闭。仅仅使用 eval() 就可能导致代码性能更差。 - user56reinstatemonica8
12
请注意,JSON方法会丢失任何在JSON中没有等价的JavaScript类型。例如:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))将生成{a: null, b: null, c: null, g: false} - oriadam
React社区已经推出了immutability-helper - Navid
67个回答

5838

本地深度克隆

现在有一个名为"结构化克隆"的JS标准,在Node 11及更高版本中进行实验,将在浏览器中使用,并且已经为现有系统提供了多种填充

structuredClone(value)

如果需要,先加载 polyfill:

import structuredClone from '@ungap/structured-clone';

查看这个答案以获取更多详细信息。

旧回答

使用JSON.parse/stringify进行快速克隆,但会丢失数据

如果您的对象中没有使用Date、函数、undefinedInfinity、RegExps、Maps、Sets、Blobs、FileLists、ImageData、稀疏数组、Typed Arrays或其他复杂类型,那么一个非常简单的一行代码可以深度克隆一个对象:

JSON.parse(JSON.stringify(object))

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
  re: /.*/,  // lost
}
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()

请参考Corban's answer获取基准测试结果。

使用库进行可靠的克隆

由于对象克隆并不是一件简单的事情(涉及到复杂类型、循环引用、函数等),大多数主要的库都提供了克隆对象的函数。 不要重复造轮子 - 如果您已经在使用库,请检查它是否有对象克隆函数。例如:


135
注意! var A = { b: [ { a: [ 1, 2, 3], b: [4, 5, 6], c: [7, 8, 9] } ] }; B = Object.assign( {}, A ); delete B.b[0].b; 它也会修改对象A! - Gabriel Hautclocq
17
这是因为A.bB.b都指向内存中的同一对象。如果A具有非对象值的属性(例如数字或字符串),则会正常复制该值。但当复制包含对象值的属性时,它是按引用而不是按值复制的。此外,请记住,在JS中,数组是一个对象。 证据:typeof [] == 'object' && [] instanceof Array - Unicornist
47
@Unicornist 是的,这就是为什么 Object.assign 不能回答这个问题:“在 JavaScript 中深克隆一个对象最有效的方法是什么?”因此,它至少不应该被呈现为 ES6 的深度克隆解决方案。标题“ES6”是误导性的,至少应该更改以反映这不是深度克隆方法。人们很容易忽略“浅层”的词语,许多人只会在 Stack Overflow 上找到最简单的解决方案而不会读完所有内容。依靠 Object.assign 进行对象克隆是危险的。因此,这是我的评论。 - Gabriel Hautclocq
6
我使用了一个叫做"really fast deep clone"的库: https://github.com/davidmarkclements/rfdc对我非常有效。 - bakkaa
3
@Ricardo,您一定可以查看答案的历史记录,看到在我发表评论后,"(shallow copy)"已添加在"ES6"之后。现在更清楚这是一个浅复制。 - Gabriel Hautclocq
显示剩余15条评论

2484

请查看这个基准测试:http://jsben.ch/#/bWfk9

在我之前的一些速度相关测试中,我发现

JSON.parse(JSON.stringify(obj))

这可能是深度克隆对象最慢的方法(比设置jQuery.extend中的deep标志为true时要慢10-20%)。

deep标志设置为false(浅拷贝)时,jQuery.extend非常快。它是一个很好的选择,因为它包括一些额外的逻辑用于类型验证,并且不会复制未定义的属性等,但这也会使速度变慢一些。

如果您知道要克隆的对象的结构或可以避免深嵌套数组,则可以编写一个简单的for (var i in obj)循环来克隆对象并检查hasOwnProperty,这将比使用jQuery快得多。

最后,如果您试图在热循环中克隆已知的对象结构,则可以通过内联克隆过程并手动构造对象来获得更高的性能。

for..in循环和检查hasOwnProperty会使JavaScript跟踪引擎的优化效果不佳,也会减慢速度。当速度是绝对必要的时候,请手动克隆。

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

请注意,不要在Date对象上使用JSON.parse(JSON.stringify(obj))方法- JSON.stringify(new Date())以ISO格式返回日期的字符串表示形式,JSON.parse()无法将其转换回Date对象。 有关更多详细信息,请参见此答案

此外,请注意,在至少Chrome 65中,本机克隆不是正确的方法。根据JSPerf的数据,通过创建新函数进行本机克隆的速度几乎比使用在所有情况下都非常快的JSON.stringify慢800倍

更新ES6

如果您正在使用Javascript ES6,请尝试使用此本机方法进行克隆或浅拷贝。

Object.assign({}, obj);

14
请注意,您的工作台有两个错误:首先,它将一些浅克隆(lodash 的 _.cloneObject.assign)与一些深克隆(JSON.parse(JSON.stringify()))进行了比较。其次,它在 lodash 中标记为“深克隆”,但实际上却只做了浅克隆。 - papillon
请注意,在相同的基准测试工具中进行测试时,对象展开 let obj2 = {...obj} 似乎比 Object.assign() 更高效。大约快20%。 - ragan
注意,关于Object.assign({}, obj} - 这只是浅拷贝而不是深度克隆。这意味着如果一个属性本身就是一个对象,你只会得到一个引用。 - Chrys G
+1000 用于内联克隆。我已经在对象列表中硬编码了已知属性的列表,并正在循环遍历它们,这给我带来了大约每毫秒3000个副本。一旦我摆脱了循环并在对象字面量中写出每个属性,速度就提高到每毫秒约200万个副本。绝对疯狂的速度增益,我从未意识到循环是如此缓慢的。 - Isaac King

658

结构化克隆

2022更新: structuredClone全局函数已经在Firefox 94、Node 17和Deno 1.14中可用。

HTML标准包括一个内部的结构化克隆/序列化算法,可以创建对象的深层副本。它仍然限于某些内置类型,但除了JSON支持的少数类型外,还支持Dates、RegExps、Maps、Sets、Blobs、FileLists、ImageDatas、稀疏数组、Typed Arrays等,未来可能还会支持更多。它还保留了克隆数据中的引用,使其能够支持JSON会导致错误的循环和递归结构。

在Node.js中的支持:

Node 17.0提供了structuredClone全局函数

const clone = structuredClone(original);

以前的版本: 在Node.js的v8模块(截至Node 11)中直接公开了结构化序列化API,但此功能仍被标记为“实验性的”,并可能在将来的版本中更改或删除。 如果您正在使用兼容版本,则克隆对象就像这样简单:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

浏览器直接支持:在Firefox 94中提供

structuredClone 全局函数很快将由所有主流浏览器提供(此前已在GitHub上讨论过)。它看起来/将会是这样的:

const clone = structuredClone(original);

在这个被提及的功能发布之前,浏览器的结构化克隆实现只能以间接的方式使用。

异步解决方案:可用。

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

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

使用示例:

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

同步解决方式:糟糕透了!

没有好的方法可以同步创建结构化克隆。以下是一些不切实际的替代方案。

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

使用示例:

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

虽然同步,但这可能非常缓慢。它产生了操作浏览器历史的所有开销。重复调用此方法可能导致Chrome暂时无响应。

Notification构造函数创建其相关数据的结构化克隆。它还尝试向用户显示浏览器通知,但如果您没有请求通知权限,则此操作将默默失败。如果您已经获得了其他目的的通知权限,我们将立即关闭创建的通知。

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

使用示例:

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


59
这太不对了!那个API不是用来这种方式使用的。 - Fardin K.
327
作为在Firefox中实现pushState的人,我对此技术的使用感到既自豪又反感。做得好,伙计们。 - Justin L.
1
pushState或Notification hack在某些对象类型(例如Function)上不起作用。 - Shishir Arora
5
2022年4月更新:structuredClone已在Firefox 94+、Chrome 98+、Safari 15.4+和Edge 98+中可用,因此在所有主流浏览器的当前版本中都可以使用! - oelna
2
以上滥用API的行为(并不是针对@Jeremy勇敢尝试提供解决方案)将会持续存在,只要HTML编写委员会基本上无法设计出高质量的API并继续推出设计有缺陷的API。例如,结构化克隆算法定义了一个过程,这个过程是严格的(很难通过脚本进行扩展),同时又给用户代理留下了太多的空间。例如,Firefox无法克隆“Error”对象,但MDN自豪地宣称支持“structuredClone”和相关功能,尽管这是一种解释方式。 - Armen Michaeli
显示剩余4条评论

566

假设你的对象仅包含属性而没有任何函数,你可以直接使用:

var newObject = JSON.parse(JSON.stringify(oldObject));

2
函数日期 也一并翻译。 - vsync
11
无法处理具有循环属性的对象。 - Koushik Shom Choudhury
1
或集合(Set)或其他不可JSON序列化的属性 - Andy Carlson
2
在我看来,这是克隆数据对象的最佳方法。特别是当您处理从API获取并进行状态管理的数据时。我相信,如果您需要克隆使用原生JavaScript结构创建的对象(函数、日期、NaN等),那么有些不对劲,或者很有可能您不需要克隆它。 - albanx
1
这对于像NaN、Infinity、undefined等类型来说是低效的。JSON.stringify将它们转换为null。参考:JSON.parse(JSON.stringify({a:null,b:undefined}))等于{a: null}。 - zlatanned

380

如果没有内置的选项,你可以尝试:

function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

6
你能解释一下isActiveClone部分的意思吗? - era s'q
它似乎可以跟踪循环引用。 例如: const a = {}; a['selfref'] = a; a['text'] = 'something'; alert(a.selfref.text);如果您尝试克隆上述 a 对象而没有 isActiveClone 部分,则会陷入无限递归(因为属性 selfref 上的递归 clone() 调用)。 - Peti29
以下是一条关于处理“数组”、“循环引用”以及具有可接受速度(性能)和清晰代码的最佳答案。 - S.Serpooshan

169

一行代码实现克隆(非深克隆)对象的高效方法

Object.assign 方法是 ECMAScript 2015 (ES6) 标准的一部分,可以完全满足你的需求。

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

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

阅读更多...

支持旧版浏览器的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;
    }
  });
}

104
这个不会递归复制,因此并没有真正提供克隆对象的解决方案。 - mwhite
7
这种方法很有效,尽管我测试了几种方法,但 _.extend({}, (obj)) 明显是最快的:比如说它比 JSON.parse 快20倍,比 Object.assign 快60%。它可以很好地复制所有子对象。 - Nico
18
@mwhite,clone 和 deep-clone 之间有区别。这个答案确实进行了克隆操作,但它并没有进行深度克隆。 - Meirion Hughes
4
这个问题是关于递归复制的。Object.assign以及给定的自定义assign都不会进行递归复制。 - johannes_lalala
这个答案不是深度克隆,也不是问题所关注的内容。 - zkldi

135

性能深拷贝:

根据基准测试,从最好到最差排序如下:
https://www.measurethat.net/Benchmarks/Show/17502/0/deep-copy-comparison

  • 扩展运算符 ...(仅适用于原始数组)
  • slice()(仅适用于原始数组)
  • splice(0)(仅适用于原始数组)
  • concat()(仅适用于原始数组)
  • JSON.parse(JSON.stringify())(仅适用于原始和字面量数组)
  • 自定义函数,如下所示(任何数组)
  • Lodash 的 _.cloneDeep()(任何数组)
  • jQuery 的 $.extend()(任何数组)
  • Underscore 的 _.clone()(仅适用于原始和字面量数组)

其中:

  • 原始值 = 字符串、数字和布尔值
  • 字面量 = 对象字面量 {},数组字面量 []
  • any = 原始值、字面量和原型

深拷贝一个原始值数组:

let arr1a = [1, 'a', true];

要深复制仅包含原始数据类型的数组(即数字、字符串和布尔值),可以使用重新赋值、slice()concat() 和 Underscore 的 clone() 方法。

在性能方面,扩展运算符是最快的:

let arr1b = [...arr1a];

在编程中,slice()splice(0)concat()具有更好的性能。

let arr1d = arr1a.slice();
let arr1c = arr1a.splice(0);
let arr1e = arr1a.concat();

深度复制一个包含基本类型和对象字面量的数组:

let arr2a = [1, 'a', true, {}, []];
let arr2b = JSON.parse(JSON.stringify(arr2a));

深度复制一个包含原始类型、对象字面量和原型的数组:

let arr3a = [1, 'a', true, {}, [], new Object()];

编写自定义函数(比jQuery的$.extend()性能更快):

function copy(aObject) {
  // Prevent undefined objects
  // if (!aObject) return aObject;

  let bObject = Array.isArray(aObject) ? [] : {};

  let value;
  for (const key in aObject) {

    // Prevent self-references to parent object
    // if (Object.is(aObject[key], aObject)) continue;
    
    value = aObject[key];

    bObject[key] = (typeof value === "object") ? copy(value) : value;
  }

  return bObject;
}

let arr3b = copy(arr3a);

或者使用第三方的实用函数:

let arr3c = $.extend(true, [], arr3a); // jQuery
let arr3d = _.cloneDeep(arr3a); // Lodash

2
使用for-in循环时,应该使用hasOwnProperty来排除继承的属性。我使用(可能更快的)普通for循环遍历Object.keys - mikiqex
3
在深拷贝中,您不想同时复制继承的属性吗?此外,请注意调用 hasOwnProperty 方法会为每个键创建性能损失(将函数调用推入和弹出堆栈,并执行方法代码)。 - tim-montague
1
大多数这些都不是深克隆,因此将它们相互进行基准测试没有意义。 - zkldi
1
在我的测试中,slice()splice(0) 快得多,尽管它们都是浅拷贝。 - S.Serpooshan
@S.Serpooshan - 经过以下基准测试的更新后,答案已经得到了确认(https://www.measurethat.net/Benchmarks/Show/17502/0/deep-copy-comparison) - tim-montague
我不确定我理解为什么这个答案将其中一半称为“深拷贝”方法,因为像扩展运算符、切片等都明确是浅拷贝机制。 - undefined

122

这是我正在使用的代码:

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])=="object" && obj[i] != null)
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

尝试过程中,我使用了以下代码:var a = {b: 1, c: 3, d: { a: 10, g: 20, h: { today: new Date() }}}; 然而这并没有起作用。但是 Object.assign({}, a) 却可以。 - Martin Luther ETOUMAN
更糟糕的是,尝试 let o = {}; o.o = o; cloneObject(o); - Gershom Maes
注意:这将不适用于“日期”。 - Nate Levin
这对于数组无效,因为它会将它们转换为对象:{ a: ["foo", "bar"} } 将变成 { a { "0": "foo", "1": "bar" } } - zkldi
我已经扩展了这个函数以支持复杂对象,但目前它不支持日期。 - MohanaRajesh

115

代码:

// extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
function extend(from, to)
{
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
        from.constructor == String || from.constructor == Number || from.constructor == Boolean)
        return new from.constructor(from);

    to = to || new from.constructor();

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

    return to;
}

测试:

var obj =
{
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
        num: 234,
        text: "asdsaD"
    }
}

var clone = extend(obj);

我不知道这个处理循环结构的方式。 - Gershom Maes

103

在JavaScript中进行深层复制对象的方法(我认为是最好和最简单的方法)

1. 使用JSON.parse(JSON.stringify(object));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2.使用created方法

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. 使用 Lo-Dash 的 _.cloneDeep 链接 lodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. 使用Object.assign()方法

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

但是错误的时候

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5.使用Underscore.js的_.clone方法 链接 Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

但是错误的时候

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

JSBEN.CH性能基准测试游乐场1~3 http://jsben.ch/KVQLd JavaScript中深度复制对象的性能


1
嘿,你最后的例子是错的。我认为,在错误的例子中,你必须使用 _clone 而不是 _cloneDeep。 - kenanyildiz90
1
这个创建的方法(2.)对于数组不起作用,是吗? - Toivo Säwén
1
方法#2容易受到原型污染的攻击,类似于lodash的defaultsDeep所遭受的攻击。如果(i === '__proto__'),则不应进行复制;如果(i === 'constuctor' && typeof obj[i] === 'function'),也不应进行复制。 - Frank Fajardo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接