克隆JavaScript对象的最有效方法是什么?我已经看到obj = eval(uneval(o));
被使用,但这是非标准的,只有Firefox支持.
我做过类似的事情,obj = JSON.parse(JSON.stringify(o));
但质疑效率.
我也看到了具有各种缺陷的递归复制功能.
我很惊讶没有规范的解决方案.
注意:这是对另一个答案的回复,而不是对此问题的正确回答.如果您希望快速克隆对象,请在回答此问题时遵循Corban的建议.
我想要注意jQuery中的Date
方法只能克隆DOM元素.要克隆JavaScript对象,您可以:
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()
可以在jQuery文档中找到更多信息.
我还要注意,深拷贝实际上比上面显示的更聪明 - 它能够避免许多陷阱(例如,尝试深度扩展DOM元素).它经常在jQuery核心和插件中使用,效果很好.
查看此基准:http://jsben.ch/#/bWfk9
在我之前的测试中,速度是我发现的一个主要问题
JSON.parse(JSON.stringify(obj))
成为深度克隆对象的最快方法(它将jQuery.extend与深度标志设置为10-20%).
当深度标志设置为false(浅层克隆)时,jQuery.extend非常快.这是一个很好的选择,因为它包含一些额外的类型验证逻辑,不会复制未定义的属性等,但这也会让你慢下来.
如果您知道要尝试克隆的对象的结构,或者可以避免深层嵌套数组,则可以deep
在检查hasOwnProperty时编写一个简单的循环来克隆对象,它将比jQuery快得多.
最后,如果您尝试在热循环中克隆已知对象结构,只需嵌入克隆过程并手动构建对象,即可获得更多性能.
JavaScript跟踪引擎在优化deep
循环时很糟糕,并且检查hasOwnProperty也会减慢你的速度.当速度是绝对必须时手动克隆.
var clonedObject = { knownProp: obj.knownProp, .. }
注意false
在for (var i in obj)
对象上使用方法- for..in
以ISO格式返回日期的字符串表示,该格式JSON.parse(JSON.stringify(obj))
不会转换回Date
对象.有关详细信息,请参阅此答案.
此外,请注意,至少在Chrome 65中,本机克隆是不可取的.根据这个JSPerf,通过创建一个新函数来执行本机克隆比使用JSON.stringify慢了近800倍,而JSON.stringify在整个过程中都非常快.
假设您的对象中只有变量而不是任何函数,您可以使用:
var newObject = JSON.parse(JSON.stringify(oldObject));
HTML标准包括一个内部结构化克隆/序列化算法,可以创建对象的深层克隆.它仍然局限于某些内置类型,但除了JSON支持的少数类型之外,它还支持日期,RegExps,地图,集合,Blob,文件列表,ImageDatas,稀疏数组,类型化数组,以及未来可能更多.它还保留了克隆数据中的引用,允许它支持会导致JSON错误的循环和递归结构.
v8
Node.js中的模块(从节点11开始)直接公开结构化序列化API,但此功能仍标记为"实验性",并且在将来的版本中可能会更改或删除.如果您使用的是兼容版本,则克隆对象非常简单:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
浏览器目前不为结构化克隆算法提供直接接口,但在GitHub上的whatwg/html#793中structuredClone()
讨论了全局函数.正如目前提出的那样,在大多数情况下使用它将如下所示:
const clone = structuredClone(original);
除非发布此内容,否则浏览器的结构化克隆实现仅间接公开.
使用现有API创建结构化克隆的低开销方法是通过MessageChannel的一个端口发布数据.另一个端口将发出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);
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();
如果没有内置的,你可以尝试:
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; }
一种Object.assign
方法是ECMAScript 2015(ES6)标准的一部分,完全符合您的需求.
var clone = Object.assign({}, obj);
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; } }); }
码:
// 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);
这就是我正在使用的:
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; }
按性能深度复制: 从最好到最差排名
重新分配"="(字符串数组,数字数组 - 仅)
切片(字符串数组,数字数组 - 仅)
连接(字符串数组,数字数组 - 仅)
自定义函数:for循环或递归复制
jQuery的$ .extend
JSON.parse(字符串数组,数字数组,对象数组 - 仅)
Underscore.js的_.clone(字符串数组,若干阵列-只)
Lo-Dash的_.cloneDeep
深层复制一个字符串或数字数组(一个级别 - 没有引用指针):
当数组包含数字和字符串时 - 函数如.slice(),. concat(),. splice(),赋值运算符"="和Underscore.js的克隆函数; 将制作数组元素的深层副本.
重新分配的性能最快:
var arr1 = ['a', 'b', 'c']; var arr2 = arr1; arr1 = ['a', 'b', 'c'];
并且.slice()的性能优于.concat(),http: //jsperf.com/duplicate-array-slice-vs-concat/3
var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c'] var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy
深层复制一个对象数组(两个或多个级别 - 引用指针):
var arr1 = [{object:'a'}, {object:'b'}];
编写自定义函数(比$ .extend()或JSON.parse具有更快的性能):
function copy(o) { var out, v, key; out = Array.isArray(o) ? [] : {}; for (key in o) { v = o[key]; out[key] = (typeof v === "object" && v !== null) ? copy(v) : v; } return out; } copy(arr1);
使用第三方实用程序功能:
$.extend(true, [], arr1); // Jquery Extend JSON.parse(arr1); _.cloneDeep(arr1); // Lo-dash
jQuery的$ .extend具有更好的性能:
http://jsperf.com/js-deep-copy/2
http://jsperf.com/jquery-extend-vs-json-parse/2
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});
有一个库(称为"克隆"),它做得很好.它提供了我所知道的最完整的递归克隆/复制任意对象.它还支持循环引用,但尚未涵盖其他答案.
你也可以在npm找到它.它可以用于浏览器以及Node.js.
以下是如何使用它的示例:
安装它
npm install clone
或用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' } }
(免责声明:我是图书馆的作者.)
我知道这是一个老帖子,但我认为这可能对下一个偶然发现的人有所帮助.
只要您不将对象分配给任何内容,它就不会在内存中保留任何引用.因此,要创建一个您想要在其他对象之间共享的对象,您必须创建一个这样的工厂:
var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father);
Cloning
一个对象在JS中始终是一个问题,但它完全是在ES6之前,我在下面列出了在JavaScript中复制对象的不同方法,想象你有下面的Object,并希望有一个深层副本:
var obj = {a:1, b:2, c:3, d:4};
复制此对象的方法很少,无需更改原点:
1)ES5 +,使用简单的功能为您复制:
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] = cloneSO(obj[i]); } return copy; } 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 this object."); }
2)ES5 +,使用JSON.parse和JSON.stringify.
var deepCopyObj = JSON.parse(JSON.stringify(obj));
3)AngularJs:
var deepCopyObj = angular.copy(obj);
4)jQuery:
var deepCopyObj = jQuery.extend(true, {}, obj);
5)UnderscoreJs&Loadash:
var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy
希望这些帮助......
如果您正在使用它,Underscore.js库有一个克隆方法.
var newObject = _.clone(oldObject);
在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链接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.)
参考medium.com
JSBEN.CH Performance Benchmarking Playground 1~3 http://jsben.ch/KVQLd
这是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; }
我的simpleoo库中也提供了此功能.
编辑:
这是一个更强大的版本(感谢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(); }; }
以下内容创建同一对象的两个实例.我发现它并且正在使用它.它简单易用.
var objToCreate = JSON.parse(JSON.stringify(cloneThis));
Lodash有一个很好的_.cloneDeep(value)方法:
var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false
Crockford建议(我更喜欢)使用这个功能:
function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject);
它很简洁,按预期工作,你不需要一个库.
编辑:
这是一个polyfill Object.create
,所以你也可以使用它.
var newObject = Object.create(oldObject);
注意: 如果您使用其中一些,您可能会遇到使用某些迭代的问题hasOwnProperty
.因为,create
创建继承的新空对象oldObject
.但它对于克隆对象仍然有用且实用.
例如,如果 oldObject.a = 5;
newObject.a; // is 5
但:
oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false
function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; }
浅拷贝单行(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
浅拷贝单行(ECMAScript第6版,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
仅仅因为我没有看到AngularJS提到并认为人们可能想知道......
angular.copy
还提供了深度复制对象和数组的方法.
对于类似数组的对象,似乎还没有理想的深度克隆运算符.如下面的代码所示,John Resig的jQuery克隆器将具有非数字属性的数组转换为非数组的对象,RegDwight的JSON克隆器删除非数字属性.以下测试在多个浏览器上说明了这些要点:
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)
根据您的目标是否克隆"普通的旧JavaScript对象",我有两个很好的答案.
我们还假设您的目的是创建一个完整的克隆,其中没有原型引用返回源对象.如果你对一个完整的克隆不感兴趣,那么你可以使用其他一些答案中提供的许多Object.clone()例程(Crockford的模式).
对于普通的旧JavaScript对象,在现代运行时克隆对象的一种经过验证的好方法非常简单:
var clone = JSON.parse(JSON.stringify(obj));
请注意,源对象必须是纯JSON对象.这就是说,它的所有嵌套属性都必须是标量(如boolean,string,array,object等).任何函数或特殊对象(如RegExp或Date)都不会被克隆.
它有效吗?哎呀.我们已经尝试了各种克隆方法,这种方法效果最好.我相信一些忍者可以想出一个更快的方法.但我怀疑我们谈论的是边际收益.
这种方法简单易行.将它包装成一个便利功能,如果你真的需要挤出一些收益,请稍后再去.
现在,对于非纯JavaScript对象,没有一个非常简单的答案.实际上,由于JavaScript函数和内部对象状态的动态特性,不可能存在.深入克隆具有内部函数的JSON结构需要重新创建这些函数及其内部上下文.而JavaScript根本没有标准化的方法.
再次执行此操作的正确方法是通过在代码中声明和重用的便捷方法.方便的方法可以让您对自己的对象有所了解,这样您就可以确保在新对象中正确地重新创建图形.
我们写的是自己的,但我在这里看到了最好的一般方法:
http://davidwalsh.name/javascript-clone
这是正确的想法.作者(David Walsh)评论了广义函数的克隆.根据您的使用情况,您可以选择这样做.
主要思想是你需要特殊地处理你的函数(或原型类,即可以说)的实例化.在这里,他提供了几个RegExp和Date的例子.
这段代码不仅简短,而且非常易读.这很容易扩展.
这有效吗?哎呀.鉴于目标是生成真正的深拷贝克隆,那么您将不得不遍历源对象图的成员.使用此方法,您可以确切地调整要处理的子成员以及如何手动处理自定义类型.
你去吧 两种方法.在我看来,两者都很有效.
这通常不是最有效的解决方案,但它可以满足我的需求.简单的测试用例如下......
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
好吧,如果你使用角度,你也可以做到这一点
var newObject = angular.copy(oldObject);
// 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; };
我在这里以最大的选票不同意答案.一个递归深克隆是要快得多比JSON.parse(JSON.stringify(OBJ))中提到的方法.
Jsperf在这里排名第一:https://jsperf.com/deep-copy-vs-json-stringify-json-parse/5
从上面的答案更新的Jsben表明,递归的深度克隆击败了所有其他提到的:http://jsben.ch/13YKQ
这是快速参考的功能:
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 }
对于想要使用该JSON.parse(JSON.stringify(obj))
版本但不丢失Date对象的人,可以使用方法的第二个参数parse
将字符串转换回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(x), function(k, v) { if (typeof v === 'string' && regExp.test(v)) return new Date(v); return v; }); }
这是一个可以克隆任何JavaScript对象的综合clone()方法.它几乎处理所有情况:
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; };
只有当你可以使用ECMAScript 6或者转发器时.
特征:
复制时不会触发getter/setter.
保留getter/setter.
保留原型信息.
适用于对象文字和功能 OO书写风格.
码:
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; }
我使用npm克隆库.显然它也适用于浏览器.
https://www.npmjs.com/package/clone
let a = clone(b)
我迟到了回答这个问题,但我还有另一种克隆对象的方法:
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);
我已对代码进行了基准测试,您可以在此处测试结果:
并分享结果: 参考文献:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
在2019年我正在使用:
deepCopy(object) { const getCircularReplacer = () => { const seen = new WeakSet(); return (key, value) => { if(typeof value === 'object' && value !== null) { if(seen.has(value)) return; seen.add(value); } return value; }; }; return JSON.parse(JSON.stringify(object, getCircularReplacer())); }
const theCopy = deepCopy(originalObject);
我通常使用var newObj = JSON.parse( JSON.stringify(oldObje) );
但是,这是一个更正确的方法:
var o = {}; var oo = Object.create(o); (o === oo); // => false
观看传统浏览器!
使用今天的JavaScript克隆对象:ECMAScript 2015(以前称为ECMAScript 6)
var original = {a: 1}; // Method 1: New object with original assigned. var copy1 = Object.assign({}, original); // Method 2: New object with spread operator assignment. var copy2 = {...original};
旧浏览器可能不支持ECMAScript 2015.一个常见的解决方案是使用像Babel这样的JavaScript到JavaScript编译器输出您的JavaScript代码的ECMAScript 5版本.
正如@ jim-hall所指出的,这只是一个浅薄的副本.属性的属性被复制为引用:更改属性将更改另一个对象/实例中的值.
单行ECMAScript 6解决方案(特殊对象类型,如Date/Regex未处理):
const clone = (o) =>
typeof o === 'object' && o !== null ? // only clone objects
(Array.isArray(o) ? // if cloning an array
o.map(e => clone(e)) : // clone each of its elements
Object.keys(o).reduce( // otherwise reduce every key in the object
(r, k) => (r[k] = clone(o[k]), r), {} // and save its cloned value into a new object
)
) :
o; // return non-objects as is
var x = {
nested: {
name: 'test'
}
};
var y = clone(x);
console.log(x.nested !== y.nested);
Lodash有一个功能,可以为你处理.
var foo = {a: 'a', b: {c:'d', e: {f: 'g'}}}; var bar = _.cloneDeep(foo); // bar = {a: 'a', b: {c:'d', e: {f: 'g'}}}
阅读这里的文档.
那么由a完成的异步对象克隆Promise
呢?
async function clone(thingy /**/) { if(thingy instanceof Promise) { throw Error("This function cannot clone Promises."); } return thingy; }
ES 2017示例:
let objectToCopy = someObj; let copyOfObject = {}; Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(objectToCopy)); // copyOfObject will now be the same as objectToCopy
以我的经验,递归版本的性能要好得多JSON.parse(JSON.stringify(obj))
。这是现代化的递归深层对象复制功能,可以放在一行上:
function deepCopy(obj) { return Object.keys(obj).reduce((v, d) => Object.assign(v, { [d]: (obj[d].constructor === Object) ? deepCopy(obj[d]) : obj[d] }), {}); }
它的执行速度比该JSON.parse...
方法快40倍。