什么是最有效的方式来克隆一个JavaScript对象?我已经看到obj = eval(uneval(o));被使用,但它是非标准的,仅支持Firefox.我做了事情,如obj = JSON.parse(JSON.stringify(o));但质疑效率。
当前回答
我的场景略有不同,我有一個物體,擁有<unk>的物體以及功能,因此,Object.assign() 和 JSON.stringify() 並沒有解決我的問題。
因此,我决定做一个简单的功能,使用内置的方法来复制一个对象,其字面特性,其粘着的对象和功能。
let deepCopy = (target, source) => {
Object.assign(target, source);
// check if there's any nested objects
Object.keys(source).forEach((prop) => {
/**
* assign function copies functions and
* literals (int, strings, etc...)
* except for objects and arrays, so:
*/
if (typeof(source[prop]) === 'object') {
// check if the item is, in fact, an array
if (Array.isArray(source[prop])) {
// clear the copied referenece of nested array
target[prop] = Array();
// iterate array's item and copy over
source[prop].forEach((item, index) => {
// array's items could be objects too!
if (typeof(item) === 'object') {
// clear the copied referenece of nested objects
target[prop][index] = Object();
// and re do the process for nested objects
deepCopy(target[prop][index], item);
} else {
target[prop].push(item);
}
});
// otherwise, treat it as an object
} else {
// clear the copied referenece of nested objects
target[prop] = Object();
// and re do the process for nested objects
deepCopy(target[prop], source[prop]);
}
}
});
};
下面是测试代码:
let a = {
name: 'Human',
func: () => {
console.log('Hi!');
},
prop: {
age: 21,
info: {
hasShirt: true,
hasHat: false
}
},
mark: [89, 92, { exam: [1, 2, 3] }]
};
let b = Object();
deepCopy(b, a);
a.name = 'Alien';
a.func = () => { console.log('Wassup!'); };
a.prop.age = 1024;
a.prop.info.hasShirt = false;
a.mark[0] = 87;
a.mark[1] = 91;
a.mark[2].exam = [4, 5, 6];
console.log(a); // updated props
console.log(b);
对于效率相关的担忧,我认为这是我遇到的问题的最简单和最有效的解决方案,我会欣赏任何关于这个算法的评论,这可能会使它更有效。
其他回答
有很多答案,但没有一个给了我需要的效果,我想利用jQuery的深复制的力量......但是,当它进入一个序列时,它只是复制了对序列的参考,并深复制了其中内容。
(它甚至检查 kendo.data.ObservableArray 如果你想要它! 但是, 确保你打电话 kendo.observable(newItem) 如果你想 Arrays 再次可观察。
所以,要完全复制一个现有项目,你只是这样做:
var newItem = jQuery.extend(true, {}, oldItem);
createNewArrays(newItem);
function createNewArrays(obj) {
for (var prop in obj) {
if ((kendo != null && obj[prop] instanceof kendo.data.ObservableArray) || obj[prop] instanceof Array) {
var copy = [];
$.each(obj[prop], function (i, item) {
var newChild = $.extend(true, {}, item);
createNewArrays(newChild);
copy.push(newChild);
});
obj[prop] = copy;
}
}
}
这里是一个全面的克隆()方法,可以克隆任何JavaScript对象,它处理几乎所有情况:
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;
};
代码:
// 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);
如何在一行代码中克隆(而不是深克隆)对象
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;
}
});
}
对于未来的参考,目前的 ECMAScript 6 草案将 Object.assign 引入为克隆对象的一种方式。
var obj1 = { a: true, b: 1 };
var obj2 = Object.assign(obj1);
console.log(obj2); // { a: true, b: 1 }
在编写时,支持仅限于Firefox 34在浏览器中,所以它还不能在生产代码中使用(除非您正在编写Firefox扩展)。