什么是最有效的方式深深的克隆的对象JavaScript?
-
02-07-2019 - |
题
什么是最有效的方式克隆JavaScript对象?我已经看到了 obj = eval(uneval(o));
正在使用,但是 这是非的标准和只支持的火狐.
我喜欢做的事情 obj = JSON.parse(JSON.stringify(o));
但问题的效率。
我也见过递归的复印职能的各种缺陷。
我很惊讶没有规范解决方案的存在。
解决方案
2019年六月注意: 这原本是一个回答另一个答案,不是一个适当的响应这个问题。不知道为什么它被选中作为正确的答案。但是,由于"顶"滚存和它迄今为止#1的回答这个问题,它将总结的解决方案作为一个wiki的答案。
本机深克隆
这就是所谓的"结构性克隆",作实验中的节点11和后来,并希望将土地浏览器。看看 这个答案 更多的细节。
快克隆与数据丢失的手机中。分析/转换成字符串
如果不使用 Date
s职能, undefined
, Infinity
,Regexp、地图集、Blob 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()
看看 对的答案 对于基准。
可靠性克隆使用的图书馆
由于克隆的对象不是微不足道(复杂的类型,圆形的参考文献,功能等等), 最主要的图书馆提供的功能,以克隆的对象。 不要重新发明轮子 -如果你已经在使用图书馆,检查,如果它有一个目的克隆的功能。例如,
- lodash-
cloneDeep
;可以进口分别通过 lodash.clonedeep 模块,可能是你最好的选择,如果你不是已经使用图书馆,提供了一个深刻的克隆功能 - 角
angular.copy
- jQuery
jQuery.extend(true, { }, oldObject)
;.clone()
只有克隆DOM元素
ES6
为完整起见,注意ES6提供两个浅复制机制: Object.assign()
和 传播运营商.
其他提示
查看此基准: http://jsben.ch/#/bWfk9
在我之前的测试中,速度是我发现的一个主要问题
JSON.parse(JSON.stringify(obj))
是深度克隆对象的最慢方式(它慢于 jQuery.extend deep
标志设置为10-20%的真实值。
deep
标志设置为 false
(浅层克隆)时,jQuery.extend非常快。这是一个很好的选择,因为它包含一些额外的类型验证逻辑,不会复制未定义的属性等,但这也会让你慢下来。
如果您知道要尝试克隆的对象的结构或者可以避免深层嵌套数组,则可以编写一个简单的 for(var i in obj)
循环来克隆您的对象,同时检查hasOwnProperty和它会比jQuery快得多。
最后,如果您尝试在热循环中克隆已知对象结构,只需简单地嵌入克隆过程并手动构建对象,即可获得更多性能。
JavaScript跟踪引擎很难优化 for..in
循环,并且检查hasOwnProperty也会减慢你的速度。当速度是绝对必须时手动克隆。
var clonedObject = {
knownProp: obj.knownProp,
..
}
小心使用 Date
对象上的 JSON.parse(JSON.stringify(obj))
方法 - JSON.stringify(new Date())
以ISO格式返回日期的字符串表示形式, JSON.parse()
不转换回 Date
对象。 有关详细信息,请参阅此答案一>
此外,请注意,至少在Chrome 65中,本机克隆是不可取的。根据此JSPerf ,通过创建新功能执行本机克隆几乎 800x 比使用JSON.stringify慢得多,这一切都非常快。
ES6更新 强>
如果您使用的是Javascript ES6,请尝试使用此本机方法进行克隆或浅层复制。
Object.assign({}, obj);
假设您的对象中只有变量而不是任何函数,您可以使用:
var newObject = JSON.parse(JSON.stringify(oldObject));
结构化克隆
HTML标准包括 内部结构化克隆/序列化算法 ,可以创建对象的深层克隆。它仍然局限于某些内置类型,但除了JSON支持的少数类型之外,它还支持日期,RegExps,地图,集合,Blob,FileLists,ImageDatas,稀疏数组,类型化数组,以及未来可能更多。它还保留了克隆数据中的引用,允许它支持可能导致JSON错误的循环和递归结构。
支持Node.js:实验🙂
Node.js中的 v8
模块(从节点11开始)直接公开结构化序列化API ,但此功能仍标记为“实验性”,并且在将来的版本中可能会更改或删除。如果您使用的是兼容版本,则克隆对象非常简单:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
浏览器的直接支持:可能最终? 😐
浏览器目前不提供结构化克隆算法的直接接口,但 structuredClone()函数。 html / issues / 793“rel =”noreferrer“>在GitHub上的whatwg / html#793 。正如目前提出的那样,在大多数情况下使用它将非常简单:
const clone = structuredClone(original);
除非发布此内容,否则浏览器的结构化克隆实现仅间接公开。
异步解决方法:可用。 😕
使用现有API创建结构化克隆的低开销方法是通过 MessageChannels 。另一个端口将发出 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();
虽然是同步的,但这可能会非常慢。它会产生与操纵浏览器历史记录相关的所有开销。反复调用此方法可能会导致Chrome暂时无法响应。
Notification
构造函数创建其关联数据的结构化克隆。它还会尝试向用户显示浏览器通知,但除非您已请求通知权限,否则将以静默方式失败。如果您有其他目的的许可,我们将立即关闭我们创建的通知。
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
使用示例:
'use strict';
const main = () => {
const original = { date: new Date(), number: Math.random() };
original.self = original;
const clone = structuredClone(original);
// They're different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// They're cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// They contain equivalent values:
console.assert(original.number === clone.number);
console.assert(Number(original.date) === Number(clone.date));
console.log("Assertions complete.");
};
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.close();
return n.data;
};
main();
如果没有内置的,你可以尝试:
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()方法用于将所有可枚举的自有属性的值从一个或多个源对象复制到目标对象。
支持旧浏览器的 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;
}
});
}
代码:
// 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;
}
深复制的性能: 排名从最好最坏的
- 重新分配"="(string阵列的数字阵列-only)
- 切片(string阵列的数字阵列-only)
- 联(string阵列的数字阵列-only)
- 自定义功能:对于环或递归的复制
- google i/o大会的$.延长
- 手机中。分析(string阵列的数字阵列,对象阵列-only)
- Underscore.js's_.克隆(string阵列的数字阵列-only)
- Lo划的_.cloneDeep
深复制一系列的串或数字(一个水平-没有参照指针):
当一个阵列含有数字和字符串功能等。切片(),.concat(),.拼接(),转让经营者"=",Underscore.js's克隆功能;会让一个深刻的副本列的要素。
在重新分配具有最快的性能:
var arr1 = ['a', 'b', 'c'];
var arr2 = arr1;
arr1 = ['a', 'b', 'c'];
和。切片()有更好的业绩。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'}];
写一个自定义功能(具有高性能足一美元.延长()函.分析):
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
其中google i/o大会的$.延长具有更好的业绩:
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});
有图书馆(名为&#8220;克隆&#8221;),这样做很好。它提供了我所知道的最完整的递归克隆/复制任意对象。它还支持循环引用,但尚未涵盖其他答案。
您也可以在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);
克隆
一个对象在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&amp; 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: {
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 强>
以上是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
function clone(obj)
{ var clone = {};
clone.prototype = obj.prototype;
for (property in obj) clone[property] = obj[property];
return clone;
}
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
浅拷贝单行( 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
AngularJS
如果你正在使用棱角分明,你也可以这样做
var newObject = angular.copy(oldObject);
我不同意的回答与的最大的选民 在这里,.一个 递归深克隆 是 快很多 比 手机中。分析(手机中。转换成字符串(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
}
// 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))
版本但不丢失Date对象的人,可以使用 parse
方法的第二个参数将字符串转换回日期:
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;
};