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

发布于 2025-01-21 05:48:25 字数 379 浏览 2 评论 0 原文

克隆JavaScript对象的最有效方法是什么?我已经看过 obj = eval(uneval(o)); 被使用,但是非标准,仅由firefox


我已经完成了 obj = obj = json.parse(json.stringify) (o)); ,但质疑效率。

我还看到了具有各种缺陷的递归复制功能。
我很惊讶没有规范的解决方案。

What is the most efficient way to clone a JavaScript object? I've seen obj = eval(uneval(o)); being used, but that's non-standard and only supported by Firefox.

I've done things like obj = JSON.parse(JSON.stringify(o)); but question the efficiency.

I've also seen recursive copying functions with various flaws.

I'm surprised no canonical solution exists.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

情丝乱 2025-01-28 05:48:26
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }
海之角 2025-01-28 05:48:26

lodash有一个不错的_。clonedeep(value)方法:

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

Lodash has a nice _.cloneDeep(value) method:

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
遇见了你 2025-01-28 05:48:26

对于类似阵列的对象,似乎还没有理想的深克隆操作员。如下所示,John Resig的JQuery克隆器将具有非数字属性的数组变成不是数组的对象,RegdWight的JSON Cloner删除了非数字属性。以下测试在多个浏览器上说明了这些点:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)

There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig's jQuery cloner turns arrays with non-numeric properties into objects that are not arrays, and RegDwight's JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers:

function jQueryClone(obj) {
   return jQuery.extend(true, {}, obj)
}

function JSONClone(obj) {
   return JSON.parse(JSON.stringify(obj))
}

var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
arrayLikeObj.names = ["m", "n", "o"];
var JSONCopy = JSONClone(arrayLikeObj);
var jQueryCopy = jQueryClone(arrayLikeObj);

alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
      "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
      "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
      "\nAnd what are the JSONClone names? " + JSONCopy.names)
旧话新听 2025-01-28 05:48:26

ecmascript第5版

var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

浅复制单位liner ecmascript 6th Edition 6th Edition 6th Edition 2015,2015,2015,2015年:

var origin = { foo : {} };
var copy = Object.assign({}, origin);

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

Shallow copy one-liner (ECMAScript 5th edition):

var origin = { foo : {} };
var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{});

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

And shallow copy one-liner (ECMAScript 6th edition, 2015):

var origin = { foo : {} };
var copy = Object.assign({}, origin);

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true
堇色安年 2025-01-28 05:48:26

仅仅因为我没有看到 angularjs 提到并认为人们可能想知道...

< a href =“ https://docs.angularjs.org/api/ng/function/angular.copy” rel =“ noreferrer”> angular.copy.copy 也提供了一种深层的方法复制对象和数组。

Just because I didn't see AngularJS mentioned and thought that people might want to know...

angular.copy also provides a method of deep copying objects and arrays.

避讳 2025-01-28 05:48:26

我有两个好的答案,具体取决于您的目标是克隆“普通的旧JavaScript对象”。

我们还假设您的意图是创建一个完整的克隆,而没有原型引用回到源对象。如果您对完整的克隆不感兴趣,则可以使用其他一些答案(Crockford的模式)中提供的许多object.clone()例程。

对于普通的旧javaScript对象,在现代运行时克隆对象的久经考验的好方法很简单:

var clone = JSON.parse(JSON.stringify(obj));

请注意,源对象必须是纯JSON对象。这就是说,其所有嵌套属性都必须是标量(例如布尔,字符串,数组,对象等)。任何功能或特殊对象(例如RegexP或日期)都不会克隆。

有效吗?哎呀。我们尝试了各种克隆方法,这最有效。我敢肯定,有些忍者可以使人联想到更快的方法。但是我怀疑我们正在谈论边际收益。

这种方法简单易于实现。将其包裹在便利功能中,如果您确实需要挤出一些收益,请在以后的时间内进行。

现在,对于非平台JavaScript对象,没有一个非常简单的答案。实际上,不可能是因为JavaScript函数和内部对象状态的动态性质。深层克隆具有内部功能的JSON结构需要您重新创建这些功能及其内部上下文。 JavaScript根本没有标准化的方法。

再次执行此操作的正确方法是通过您在代码中声明和重复使用的便利方法。便利方法可以赋予对自己对象的一些了解,因此您可以确保在新对象中正确重新创建图形。

我们写了自己的书,但是我看到的最好的一般方法在这里涵盖:

http://davidwalsh.name /javascript-clone

这是正确的想法。作者(David Walsh)评论了广义功能的克隆。这是您可能选择的,具体取决于您的用例。

主要思想是,您需要特殊处理功能的实例化(可以说是这样说)。在这里,他为Regexp和日期提供了一些示例。

该代码简介不仅是非常可读的。它很容易扩展。

这有效吗?哎呀。鉴于目标是生产一个真正的深拷贝克隆,那么您将不得不走源对象图的成员。使用这种方法,您可以准确调整要治疗的儿童成员以及如何手动处理自定义类型。

所以你去了。两种方法。在我看来,两者都是有效的。

I have two good answers depending on whether your objective is to clone a "plain old JavaScript object" or not.

Let's also assume that your intention is to create a complete clone with no prototype references back to the source object. If you're not interested in a complete clone, then you can use many of the Object.clone() routines provided in some of the other answers (Crockford's pattern).

For plain old JavaScript objects, a tried and true good way to clone an object in modern runtimes is quite simply:

var clone = JSON.parse(JSON.stringify(obj));

Note that the source object must be a pure JSON object. This is to say, all of its nested properties must be scalars (like boolean, string, array, object, etc). Any functions or special objects like RegExp or Date will not be cloned.

Is it efficient? Heck yes. We've tried all kinds of cloning methods and this works best. I'm sure some ninja could conjure up a faster method. But I suspect we're talking about marginal gains.

This approach is just simple and easy to implement. Wrap it into a convenience function and if you really need to squeeze out some gain, go for at a later time.

Now, for non-plain JavaScript objects, there isn't a really simple answer. In fact, there can't be because of the dynamic nature of JavaScript functions and inner object state. Deep cloning a JSON structure with functions inside requires you recreate those functions and their inner context. And JavaScript simply doesn't have a standardized way of doing that.

The correct way to do this, once again, is via a convenience method that you declare and reuse within your code. The convenience method can be endowed with some understanding of your own objects so you can make sure to properly recreate the graph within the new object.

We're written our own, but the best general approach I've seen is covered here:

http://davidwalsh.name/javascript-clone

This is the right idea. The author (David Walsh) has commented out the cloning of generalized functions. This is something you might choose to do, depending on your use case.

The main idea is that you need to special handle the instantiation of your functions (or prototypal classes, so to speak) on a per-type basis. Here, he's provided a few examples for RegExp and Date.

Not only is this code brief, but it's also very readable. It's pretty easy to extend.

Is this efficient? Heck yes. Given that the goal is to produce a true deep-copy clone, then you're going to have to walk the members of the source object graph. With this approach, you can tweak exactly which child members to treat and how to manually handle custom types.

So there you go. Two approaches. Both are efficient in my view.

守不住的情 2025-01-28 05:48:26

我迟到要回答这个问题了,但是我有另一种克隆对象的方法:

function cloneObject(obj) {
    if (obj === null || typeof(obj) !== 'object')
        return obj;
    var temp = obj.constructor(); // changed
    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = cloneObject(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

var b = cloneObject({"a":1,"b":2});   // calling

那要好得多,更快:

var a = {"a":1,"b":2};
var b = JSON.parse(JSON.stringify(a));  

var a = {"a":1,"b":2};

// Deep copy
var newObject = jQuery.extend(true, {}, a);

已经标记了代码,您可以测试结果在这里

分享结果:

参考:

I am late to answer this question, but I have an another way of cloning the object:

function cloneObject(obj) {
    if (obj === null || typeof(obj) !== 'object')
        return obj;
    var temp = obj.constructor(); // changed
    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = cloneObject(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

var b = cloneObject({"a":1,"b":2});   // calling

which is much better and faster then:

var a = {"a":1,"b":2};
var b = JSON.parse(JSON.stringify(a));  

and

var a = {"a":1,"b":2};

// Deep copy
var newObject = jQuery.extend(true, {}, a);

I have bench-marked the code and you can test the results here:

and sharing the results:
enter image description here
References: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty

埖埖迣鎅 2025-01-28 05:48:26

只有当您可以使用 ecmascript 6 transpilers

功能:

  • 复制时不会触发Getter/Setter。
  • 保留Getter/Setter。
  • 保存原型信息。
  • 对象 - 文字 functional 写作风格。

代码:

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

Only when you can use ECMAScript 6 or transpilers.

Features:

  • Won't trigger getter/setter while copying.
  • Preserves getter/setter.
  • Preserves prototype informations.
  • Works with both object-literal and functional OO writing styles.

Code:

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;
}
相权↑美人 2025-01-28 05:48:26

这通常不是最有效的解决方案,但它可以实现我需要的。下面的简单测试用例...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

循环阵列测试...

a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

功能测试...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false

This isn't generally the most efficient solution, but it does what I need. Simple test cases below...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions 
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
        already_cloned,
        t = typeof obj,
        i = 0,
        l,
        pair; 

    clones = clones || [];

    if (obj === null) {
        return obj;
    }

    if (t === "object" || t === "function") {

        // check to see if we've already cloned obj
        for (i = 0, l = clones.length; i < l; i++) {
            pair = clones[i];
            if (pair[0] === obj) {
                already_cloned = pair[1];
                break;
            }
        }

        if (already_cloned) {
            return already_cloned; 
        } else {
            if (t === "object") { // create new object
                new_obj = new obj.constructor();
            } else { // Just use functions as is
                new_obj = obj;
            }

            clones.push([obj, new_obj]); // keep track of objects we've cloned

            for (key in obj) { // clone object members
                if (obj.hasOwnProperty(key)) {
                    new_obj[key] = clone(obj[key], clones);
                }
            }
        }
    }
    return new_obj || obj;
}

Cyclic array test...

a = []
a.push("b", "c", a)
aa = clone(a)
aa === a //=> false
aa[2] === a //=> false
aa[2] === a[2] //=> false
aa[2] === aa //=> true

Function test...

f = new Function
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false
分开我的手 2025-01-28 05:48:26

对于想要使用 JSON.PARSE(JSON.STRINGIFY(OBJ))版本的人,但不会丢失日期对象,您可以使用 parse> parse methot 的第二个参数回到日期:

function clone(obj) {
  var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
  return JSON.parse(JSON.stringify(obj), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v)
    return v;
  })
}

// usage:
var original = {
 a: [1, null, undefined, 0, {a:null}, new Date()],
 b: {
   c(){ return 0 }
 }
}

var cloned = clone(original)

console.log(cloned)

For the people who want to use the JSON.parse(JSON.stringify(obj)) version, but without losing the Date objects, you can use the second argument of parse method to convert the strings back to Date:

function clone(obj) {
  var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
  return JSON.parse(JSON.stringify(obj), function(k, v) {
    if (typeof v === 'string' && regExp.test(v))
      return new Date(v)
    return v;
  })
}

// usage:
var original = {
 a: [1, null, undefined, 0, {a:null}, new Date()],
 b: {
   c(){ return 0 }
 }
}

var cloned = clone(original)

console.log(cloned)

半窗疏影 2025-01-28 05:48:26

我不同意最大的投票的答案在这里递归深克隆 json.parse(json.stringify(obj))的方法快得多。

这是快速参考的功能:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}

I disagree with the answer with the greatest votes here. A Recursive Deep Clone is much faster than the JSON.parse(JSON.stringify(obj)) approach mentioned.

And here's the function for quick reference:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}
淡水深流 2025-01-28 05:48:26
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};
// obj target object, vals source object
var setVals = function (obj, vals) {
    if (obj && vals) {
        for (var x in vals) {
            if (vals.hasOwnProperty(x)) {
                if (obj[x] && typeof vals[x] === 'object') {
                    obj[x] = setVals(obj[x], vals[x]);
                } else {
                    obj[x] = vals[x];
                }
            }
        }
    }
    return obj;
};
樱娆 2025-01-28 05:48:25

天然深克隆

现在有一个 structuredclone(value)在所有主要浏览器中支持的功能和node&gt; = 17。它具有 polyfills对于较旧的系统

structuredClone(value)

如果需要,请先加载polyfill:

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

请参阅this answer for more details, but 注意这些限制

  • 函数对象不能由结构化的克隆算法复制;试图抛出DatacLoneError异常。
  • 克隆DOM节点同样会引发DatacLoneError异常。
  • 某些对象属性未保留:
    • REGEXP对象的最后点数属性未保留。
    • 属性描述符,设定器,get子和类似的类似元数据的功能未重复。例如,如果一个对象用属性描述符标记,则将以副本读取/写入,因为这是默认值。
    • 原型链未行走或重复。

较旧的答案

与数据丢失快速克隆-JSON.PARSE/Stringify

如果您不使用 date s,functions, undefined infinity ,regexps,地图,集合,斑点,filelists,imagedatas,稀疏数组,键入数组或对象内的其他复杂类型,一个非常简单的一个衬里,可深克隆一个对象是:

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答案用于基准。

使用库的可靠克隆

由于克隆对象不是微不足道的(复杂类型,循环引用,功能等),因此大多数主要库为克隆对象提供功能。 不要重新发明轮子 - 如果您已经在使用库,请检查其是否具有对象克隆功能。例如,

  • lodash- clonedeep ;可以通过 lodash.clonedeep 模块,并且如果您是您的最佳选择,则可以是您的最佳选择尚未使用提供深克隆功能
  • ramda的库 - clone
  • angularjs- angular.copy.copy
  • a href =“ https://api.jquery.com/jquery.extend/#jquery-extend-extend-deep-target-object-object-1-objectn” rel =“ noreferrer”> jquery.extend(true,true,{} ) ; .clone()只有clones dom元素
  • 只有库 - justa克隆 ;仅做一件事的零依赖性NPM模块库的一部分。
    每种场合都有无罪的公用事业。

Native deep cloning

There's now a structuredClone(value) function supported in all major browsers and node >= 17. It has polyfills for older systems.

structuredClone(value)

If needed, loading the polyfill first:

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

See this answer for more details, but note these limitations:

  • Function objects cannot be duplicated by the structured clone algorithm; attempting to throws a DataCloneError exception.
  • Cloning DOM nodes likewise throws a DataCloneError exception.
  • Certain object properties are not preserved:
    • The lastIndex property of RegExp objects is not preserved.
    • Property descriptors, setters, getters, and similar metadata-like features are not duplicated. For example, if an object is marked readonly with a property descriptor, it will be read/write in the duplicate, since that's the default.
    • The prototype chain is not walked or duplicated.

Older answers

Fast cloning with data loss - JSON.parse/stringify

If you do not use Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays or other complex types within your object, a very simple one liner to deep clone an object is:

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

See Corban's answer for benchmarks.

Reliable cloning using a library

Since cloning objects is not trivial (complex types, circular references, function etc.), most major libraries provide function to clone objects. Don't reinvent the wheel - if you're already using a library, check if it has an object cloning function. For example,

  • lodash - cloneDeep; can be imported separately via the lodash.clonedeep module and is probably your best choice if you're not already using a library that provides a deep cloning function
  • Ramda - clone
  • AngularJS - angular.copy
  • jQuery - jQuery.extend(true, { }, oldObject); .clone() only clones DOM elements
  • just library - just-clone; Part of a library of zero-dependency npm modules that do just do one thing.
    Guilt-free utilities for every occasion.
笨死的猪 2025-01-28 05:48:25

核对此基准: http://jsben.ch/#/bwfk9

在我以前的测试中,我发现的主要问题

JSON.parse(JSON.stringify(obj))

是最慢的克隆对象的最慢方法(比 jquery.extend.extend 带有 deep 标志设置为10-20%的标志)。

deep 标志设置为 false (浅克隆)时,jquery.extend非常快。这是一个不错的选择,因为它包含一些用于类型验证的额外逻辑,并且不会复制未定义的属性等,但这也会减慢您的速度。

如果您知道要克隆的对象的结构,或者可以避免深嵌套数组,则可以为(obj中的var i in obj)编写一个简单的 循环在检查hasownproperty时克隆您的对象,它将是比jQuery快得多。

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

JavaScript Trace引擎在优化 for..in 循环和检查HasownProperty时也会减慢您的速度。当速度是绝对必须的时,手动克隆。

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

当心使用 JSON.PARSE(JSON.STRINGIFY(OBJ))日期对象上的方法 - json.stringify(new Date())返回ISO格式日期的字符串表示形式, JSON.PARSE() 不转换回 date 对象。 a>。

此外,请注意,至少在Chrome 65中,本机克隆不是必经之路。根据JSPERF的说法,通过创建新功能来执行本机克隆几乎 800X 慢。使用JSON.Stringify,它在整个台上都非常快。

如果使用JavaScript ES6,请尝试此本机方法进行克隆或浅副本。

Object.assign({}, obj);

Checkout this benchmark: http://jsben.ch/#/bWfk9

In my previous tests where speed was a main concern I found

JSON.parse(JSON.stringify(obj))

to be the slowest way to deep clone an object (it is slower than jQuery.extend with deep flag set true by 10-20%).

jQuery.extend is pretty fast when the deep flag is set to false (shallow clone). It is a good option, because it includes some extra logic for type validation and doesn't copy over undefined properties, etc., but this will also slow you down a little.

If you know the structure of the objects you are trying to clone or can avoid deep nested arrays you can write a simple for (var i in obj) loop to clone your object while checking hasOwnProperty and it will be much much faster than jQuery.

Lastly if you are attempting to clone a known object structure in a hot loop you can get MUCH MUCH MORE PERFORMANCE by simply in-lining the clone procedure and manually constructing the object.

JavaScript trace engines suck at optimizing for..in loops and checking hasOwnProperty will slow you down as well. Manual clone when speed is an absolute must.

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

Beware using the JSON.parse(JSON.stringify(obj)) method on Date objects - JSON.stringify(new Date()) returns a string representation of the date in ISO format, which JSON.parse() doesn't convert back to a Date object. See this answer for more details.

Additionally, please note that, in Chrome 65 at least, native cloning is not the way to go. According to JSPerf, performing native cloning by creating a new function is nearly 800x slower than using JSON.stringify which is incredibly fast all the way across the board.

Update for ES6

If you are using Javascript ES6 try this native method for cloning or shallow copy.

Object.assign({}, obj);
鹿童谣 2025-01-28 05:48:25

结构化的克隆

const clone = structuredClone(original);

现代浏览器和运行时间提供A ,暴露html标准的以前的内部结构化克隆/序列化算法用于创建对象的深度克隆。它仍然仅限于某些内置类型,但是除了JSON支持的少数类型外,它支持日期,左右,地图,集合,斑点,斑点,filelists,imagedatas,Imagedatas,稀疏阵列,稀疏阵列,键入阵列,可能会在以后。它还保留了克隆数据中的参考文献,从而使其能够支持会导致JSON错误的周期性和递归结构。

如果您需要支持旧的环境,其余的答案描述了在直接暴露此功能之前使用此功能的解决方法。

同步node.js解决方案:很棒!

Structured Cloning

const clone = structuredClone(original);

Modern browsers and runtimes provide a structuredClone global function, exposing the HTML standard's previously-internal structured cloning/serialization algorithm for creating deep clones of objects. It's still limited to certain built-in types, but in addition to the few types supported by JSON it supports Dates, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays, and probably more in the future. It also preserves references within the cloned data, allowing it to support cyclical and recursive structures that would cause errors for JSON.

In case you need to support older environments, the rest of this answer describes workarounds to use this capability before it was directly exposed.

Synchronous Node.js Workaround: Excellent! ????

Node.js provides a rich interface for the structured serialization API in its v8 module. Cloning an object is as simple as:

const v8 = require('v8');

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

Asynchronous Browser Workaround: Usable. ????

The lowest-overhead way to create a structured clone with previous APIs is to post the data through one port of a MessageChannels. The other port will emit a message event with a structured clone of the attached .data. Unfortunately, listening for these events is necessarily asynchronous, and the synchronous alternatives are less practical.

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

Example Use:

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

Synchronous Browser Workarounds: Awful! ????

There are no good options for creating structured clones synchronously. Here are a couple of impractical hacks instead.

history.pushState() and history.replaceState() both create a structured clone of their first argument, and assign that value to history.state. You can use this to create a structured clone of any object like this:

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

Example Use:

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

Though synchronous, this can be extremely slow. It incurs all of the overhead associated with manipulating the browser history. Calling this method repeatedly can cause Chrome to become temporarily unresponsive.

The Notification constructor creates a structured clone of its associated data. It also attempts to display a browser notification to the user, but this will silently fail unless you have requested notification permission. In case you have the permission for other purposes, we'll immediately close the notification we've created.

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

Example Use:

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

泪意 2025-01-28 05:48:25

假设您只有属性,而对象中没有任何功能,则可以使用:

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

Assuming that you have only properties and not any functions in your object, you can just use:

var newObject = JSON.parse(JSON.stringify(oldObject));
嘿咻 2025-01-28 05:48:25

如果没有内置的,您可以尝试:

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

If there wasn't any builtin one, you could try:

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;
}
雨巷深深 2025-01-28 05:48:25

克隆(而不是deep-clone)的有效方法在一行代码中的对象

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

The efficient way to clone(not deep-clone) an object in one line of code

An Object.assign method is part of the ECMAScript 2015 (ES6) standard and does exactly what you need.

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

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.

Read more...

The polyfill to support older browsers:

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;
    }
  });
}
緦唸λ蓇 2025-01-28 05:48:25

根据表演的深度复制:

根据基准测试
排名从最佳到最差。
https://www.measurethat.net/benchmarks/show/show/17502 /0/deep -copy -comporparison

  • 传播操作员 ... (原始数组 - 仅)
  • slice()(原始数组 - 仅)
  • (0)(原始数组 - 仅)
  • concat()(原始数组 - 仅)
  • json.parse(json.stringify())数组 - 唯一的)
  • 自定义函数,如下所示(
  • 任何
  • 数组 代码> _。clone()(原始和文字数组 - 仅)

其中:

  • primitives =字符串,数字和布尔值
  • literals = object triletals {} ,array literals []
  • any =原始人,文字和原型

深层复制了一系列原始词:

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

仅具有原始词(即数字,字符串和布尔人),重新分配, slice( slice)( ) concat()和UnderScore的 clone()可以使用。

差异的情况最快:

let arr1b = [...arr1a];

slice()的性能比剪接(0) and 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 $。 ):

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

Deep copy by performance:

Ranked from best to worst, based on benchmarks
https://www.measurethat.net/Benchmarks/Show/17502/0/deep-copy-comparison

  • spread operator ... (primitive arrays - only)
  • slice() (primitive arrays - only)
  • splice(0) (primitive arrays - only)
  • concat() (primitive arrays - only)
  • JSON.parse(JSON.stringify()) (primitive and literal arrays - only)
  • custom function, as seen below (any array)
  • Lodash's _.cloneDeep() (any array)
  • jQuery's $.extend() (any array)
  • Underscore's _.clone() (primitive and literal arrays - only)

Where:

  • primitives = strings, numbers, and booleans
  • literals = object literals {}, array literals []
  • any = primitives, literals, and prototypes

Deep copy an array of primitives:

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

To deep copy arrays with primitives only (i.e. numbers, strings, and booleans), reassignment, slice(), concat(), and Underscore's clone() can be used.

Where spread has the fastest performance:

let arr1b = [...arr1a];

And where slice() has better performance than splice(0) and concat()

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

Deep copy an array of primitive and object literals:

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

Deep copy an array of primitive, object literals, and prototypes:

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

Write a custom function (has faster performance than 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);

Or use third-party utility functions:

let arr3c = $.extend(true, [], arr3a); // jQuery
let arr3d = _.cloneDeep(arr3a); // Lodash
感情废物 2025-01-28 05:48:25

这就是我正在使用的:

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

This is what I'm using:

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;
}
我不吻晚风 2025-01-28 05:48:25

代码:

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

Code:

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

Test:

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);
謸气贵蔟 2025-01-28 05:48:25

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.创建的方法

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 link link 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 performance bench performance playgronging 1〜3 http://jsben.ch/kvqld

Deep copying objects in JavaScript (I think the best and the simplest)

1. Using 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.Using created method

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. Using Lo-Dash's _.cloneDeep link 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. Using Object.assign() method

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 }  

BUT WRONG WHEN

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.Using Underscore.js _.clone link 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 }  

BUT WRONG WHEN

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 Performance Benchmarking Playground 1~3 http://jsben.ch/KVQLd
Performance Deep copying objects in JavaScript

恋竹姑娘 2025-01-28 05:48:25

克隆一个对象在JS中始终是一个关注的问题,但是在ES6之前,我列出了在下面的JavaScript中复制对象的不同方法,请想象您有以下对象,并且想对此进行深刻的副本

var obj = {a:1, b:2, c:3, d:4};

:复制此对象的方法,而无需更改原点:

  1. es5+,使用一个简单的函数为您完成副本:

     功能DeepCopyObj(obj){
        if(null == obj ||“对象”!= obj typeof obj)返回obj;
        if(obj instance of date){
            var copy = new Date();
            copy.setTime(obj.getTime());
            返回副本;
        }
        if(obj instance of array){
            var copy = [];
            for(var i = 0,len = obj.length; i&lt; len; i ++){
                复制[i] = deepCopyObj(obj [i]);
            }
            返回副本;
        }
        if(obj instensof object){
            var copy = {};
            对于(obj中的var attr){
                if(obj.hasownproperty(attr))复制[attr] = deepCopyObj(obj [attr]);
            }
            返回副本;
        }
        提出新的错误(“无法复制此对象。”);
    }
     
  2. es5+,使用 json.parse json.stringify

      var deepCopyObj = json.parse(json.stringify(obj));
     
  3. 角:

      var deepcopyobj = angular.copy(obj);
     
  4. jQuery:

      var deepCopyObj = jquery.extend(true,{},obj);
     
  5. inscore.js&amp; lodash:

      var deepcopyobj = _.clonedeep(obj); //最新版本的unterscore.js制作浅副本
     

希望这些帮助…

Cloning an object was always a concern in JS, but it was all about before ES6, I list different ways of copying an object in JavaScript below, imagine you have the Object below and would like to have a deep copy of that:

var obj = {a:1, b:2, c:3, d:4};

There are few ways to copy this object, without changing the origin:

  1. ES5+, Using a simple function to do the copy for you:

    function deepCopyObj(obj) {
        if (null == obj || "object" != typeof obj) return obj;
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                copy[i] = deepCopyObj(obj[i]);
            }
            return copy;
        }
        if (obj instanceof Object) {
            var copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) copy[attr] = deepCopyObj(obj[attr]);
            }
            return copy;
        }
        throw new Error("Unable to copy obj this object.");
    }
    
  2. ES5+, using JSON.parse and JSON.stringify.

    var deepCopyObj = JSON.parse(JSON.stringify(obj));
    
  3. Angular:

    var deepCopyObj = angular.copy(obj);
    
  4. jQuery:

    var deepCopyObj = jQuery.extend(true, {}, obj);
    
  5. Underscore.js & Lodash:

    var deepCopyObj = _.cloneDeep(obj); //latest version of Underscore.js makes shallow copy
    

Hope these help…

何其悲哀 2025-01-28 05:48:25
var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
var clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        }
        else
        {
            newObj[i] = this[i];
        }
    }
    return newObj;
}; 

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});
等待圉鍢 2025-01-28 05:48:25

有一个 library> library(称为“ clone”),这做得很好。它提供了我所知道的任意对象的最完整的递归克隆/复制。它还支持循环引用,而其他答案还没有涵盖。

您可以在npm 上也找到它。它可用于浏览器和node.js。

这是一个有关如何使用它的示例:

npm install clone

ender”> ender

ender build clone [...]

您也可以手动下载源代码。

然后,您可以在源代码中使用它。

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(免责声明:我是图书馆的作者。)

There’s a library (called “clone”), that does this quite well. It provides the most complete recursive cloning/copying of arbitrary objects that I know of. It also supports circular references, which is not covered by the other answers, yet.

You can find it on npm, too. It can be used for the browser as well as Node.js.

Here is an example on how to use it:

Install it with

npm install clone

or package it with Ender.

ender build clone [...]

You can also download the source code manually.

Then you can use it in your source code.

var clone = require('clone');

var a = { foo: { bar: 'baz' } };  // inital value of a
var b = clone(a);                 // clone a -> b
a.foo.bar = 'foo';                // change a

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(Disclaimer: I’m the author of the library.)

白昼 2025-01-28 05:48:25

我知道这是一个古老的帖子,但我认为这可能对下一个偶然的人有帮助。

只要您不将对象分配给任何内容都没有内存中的参考。因此,要制作要在其他对象之间共享的对象,您必须创建一个工厂:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);

I know this is an old post, but I thought this may be of some help to the next person who stumbles along.

As long as you don't assign an object to anything it maintains no reference in memory. So to make an object that you want to share among other objects, you'll have to create a factory like so:

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);
紫竹語嫣☆ 2025-01-28 05:48:25

如果您正在使用它,则 underscore.js 库具有克隆方法。

var newObject = _.clone(oldObject);

If you're using it, the Underscore.js library has a clone method.

var newObject = _.clone(oldObject);
美人迟暮 2025-01-28 05:48:25

这是上面的ConRoyp答案的一个版本,即使构造函数需要参数,也可以使用:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

此函数也可以在我的图书馆。

编辑:

这是一个更强大的版本(感谢Justin McCandless,现在也支持循环参考):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

Here's a version of ConroyP's answer above that works even if the constructor has required parameters:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

function deepCopy(obj) {
    if(obj == null || typeof(obj) !== 'object'){
        return obj;
    }
    //make sure the returned object has the same prototype as the original
    var ret = object_create(obj.constructor.prototype);
    for(var key in obj){
        ret[key] = deepCopy(obj[key]);
    }
    return ret;
}

This function is also available in my simpleoo library.

Edit:

Here's a more robust version (thanks to Justin McCandless this now supports cyclic references as well):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _visited, _copiesVisited) {
    if(src === null || typeof(src) !== 'object'){
        return src;
    }

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Date
    if(src instanceof Date){
        return new Date(src.getTime());
    }
    //RegExp
    if(src instanceof RegExp){
        return new RegExp(src);
    }
    //DOM Element
    if(src.nodeType && typeof src.cloneNode == 'function'){
        return src.cloneNode(true);
    }

    // Initialize the visited objects arrays if needed.
    // This is used to detect cyclic references.
    if (_visited === undefined){
        _visited = [];
        _copiesVisited = [];
    }

    // Check if this object has already been visited
    var i, len = _visited.length;
    for (i = 0; i < len; i++) {
        // If so, get the copy we already made
        if (src === _visited[i]) {
            return _copiesVisited[i];
        }
    }

    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice() by itself would soft clone
        var ret = src.slice();

        //add it to the visited array
        _visited.push(src);
        _copiesVisited.push(ret);

        var i = ret.length;
        while (i--) {
            ret[i] = deepCopy(ret[i], _visited, _copiesVisited);
        }
        return ret;
    }

    //If we've reached here, we have a regular object

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var dest = object_create(proto);

    //add this object to the visited array
    _visited.push(src);
    _copiesVisited.push(dest);

    for (var key in src) {
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        dest[key] = deepCopy(src[key], _visited, _copiesVisited);
    }
    return dest;
}

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}
静谧幽蓝 2025-01-28 05:48:25

以下创建了同一对象的两个实例。我找到了它,目前正在使用它。它简单易用。

var objToCreate = JSON.parse(JSON.stringify(cloneThis));

The following creates two instances of the same object. I found it and am using it currently. It's simple and easy to use.

var objToCreate = JSON.parse(JSON.stringify(cloneThis));
不再让梦枯萎 2025-01-28 05:48:25

克罗克福德(Crockford)建议(我更喜欢)使用此功能:

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

var newObject = object(oldObject);

它是简短的,按预期工作,您不需要库。


编辑:

这是 object.create.create 的多填充,因此您也可以使用此功能。

var newObject = Object.create(oldObject);

注意:如果您使用其中的某些,则可能有一些使用 HasownProperty 的迭代问题。因为,创建创建新的空对象谁继承了 oldobject 。但是它仍然对克隆对象有用且实用。

如果 oldobject.a = 5;

newObject.a; // is 5

but:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false

Crockford suggests (and I prefer) using this function:

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

var newObject = object(oldObject);

It's terse, works as expected and you don't need a library.


EDIT:

This is a polyfill for Object.create, so you also can use this.

var newObject = Object.create(oldObject);

NOTE: If you use some of this, you may have problems with some iteration who use hasOwnProperty. Because, create create new empty object who inherits oldObject. But it is still useful and practical for cloning objects.

For exemple if oldObject.a = 5;

newObject.a; // is 5

but:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文