obj = eval(uneval(o));
,但这是非标准的,只有Firefox支持。我已经尝试过像
obj = JSON.parse(JSON.stringify(o));
这样的方式,但质疑其效率。我也看到了递归复制函数的各种缺陷。
我很惊讶没有一个权威的解决方案存在。
obj = eval(uneval(o));
,但这是非标准的,只有Firefox支持。obj = JSON.parse(JSON.stringify(o));
这样的方式,但质疑其效率。在ES6之前,JavaScript中克隆对象一直是个问题。我列出了下面不同的方式来复制一个JavaScript对象。假设你有以下对象,并希望进行深拷贝:
var obj = {a:1, b:2, c:3, d:4};
有几种方法可以复制该对象,而不改变其原始状态:
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] = 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.");
}
使用ES5+,使用JSON.parse
和JSON.stringify
。
var deepCopyObj = JSON.parse(JSON.stringify(obj));
Angular:
var deepCopyObj = angular.copy(obj);
jQuery:
var deepCopyObj = jQuery.extend(true, {}, obj);
Underscore.js和Lodash:
var deepCopyObj = _.cloneDeep(obj); //latest version of Underscore.js makes shallow copy
希望这些能有所帮助...
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});
有一个名为“clone”的库可以很好地完成这项任务。它提供了我所知道的最完整的递归克隆/复制任意对象的功能。它还支持循环引用,这是其他答案没有涉及到的。
你也可以在npm上找到它。它既可以用于浏览器,也可以用于Node.js。
以下是如何使用它的示例:
使用以下命令进行安装:
npm install clone
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);
//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 https://dev59.com/A3RB5IYBdhLWcg3wET5J#11621004
*/
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));
Crockford提议(我也如此建议)使用以下函数:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var newObject = object(oldObject);
这个方法简洁明了,符合预期,并且不需要使用库。
编辑:
这是一个Object.create
的polyfill,所以你也可以使用它。
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;
}
Lodash有一个不错的_.cloneDeep(value)方法:
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
eval()
通常是不明智的,因为许多JavaScript引擎的优化器必须在处理通过eval()
设置的变量时关闭。仅仅使用eval()
就可能导致代码性能更差。 - user56reinstatemonica8JSON
方法会丢失任何在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