min*_*vor 2922 javascript clone javascript-objects
我有一个对象,x.我想把它复制为对象y,这样改变y就不要修改了x.我意识到复制从内置JavaScript对象派生的对象将导致额外的,不需要的属性.这不是问题,因为我正在复制我自己的一个文字构造的对象.
如何正确克隆JavaScript对象?
A. *_*evy 1495
为JavaScript中的任何对象执行此操作不会简单或直接.您将遇到错误地从对象原型中拾取属性的问题,该属性应保留在原型中而不会复制到新实例.例如,如果您要添加clone方法Object.prototype,如某些答案所示,则需要显式跳过该属性.但是,如果还有其他额外的方法Object.prototype,或其他中间原型,你不知道怎么办?在这种情况下,您将复制不应该的属性,因此您需要使用该hasOwnProperty方法检测无法预料的非本地属性.
除了不可枚举的属性,当您尝试复制具有隐藏属性的对象时,您将遇到更严峻的问题.例如,prototype是函数的隐藏属性.此外,对象的原型使用属性进行引用,该属性__proto__也是隐藏的,并且不会通过迭代源对象属性的for/in循环进行复制.我认为__proto__可能是Firefox的JavaScript解释器特有的,它可能在其他浏览器中有所不同,但是你可以了解它.并非一切都是可以计算的.如果您知道其名称,则可以复制隐藏属性,但我不知道有任何方法可以自动发现它.
寻求优雅解决方案的另一个障碍是正确设置原型继承的问题.如果您的源对象的原型是Object,那么只需创建一个新的通用对象{}将工作,但如果源的原型是其后代Object,那么您将错过使用hasOwnProperty过滤器跳过的原型中的其他成员,或者在原型中,但首先不是可枚举的.一种解决方案可能是调用源对象的constructor属性来获取初始复制对象,然后复制属性,但是您仍然不会获得不可枚举的属性.例如,Date对象将其数据存储为隐藏成员:
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
var d1 = new Date();
/* Executes function after 5 seconds. */
setTimeout(function(){
var d2 = clone(d1);
alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);
Run Code Online (Sandbox Code Playgroud)
日期字符串d1将落后5秒d2.使一个Date与另一个相同的setTime方法是调用方法,但这是特定于Date类的.我不认为这个问题有防弹的一般解决方案,但我会很高兴出错!
当我不得不实施一般深度复制我最终通过假设我只需要复制一个普通的妥协Object,Array,Date,String,Number,或Boolean.最后3种类型是不可变的,所以我可以执行浅拷贝而不用担心它会改变.我进一步假设该列表中包含的6个简单类型中的任何元素Object或者Array也将是其中之一.这可以使用以下代码完成:
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
Run Code Online (Sandbox Code Playgroud)
只要对象和数组中的数据形成树结构,上述函数就可以适用于我提到的6种简单类型.也就是说,对象中的相同数据的引用不超过一个.例如:
// This would be cloneable:
var tree = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"right" : null,
"data" : 8
};
// This would kind-of work, but you would get 2 copies of the
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];
// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cyclicGraph["right"] = cyclicGraph;
Run Code Online (Sandbox Code Playgroud)
它将无法处理任何JavaScript对象,但它可能足以用于许多目的,只要您不认为它只适用于您抛出的任何内容.
hei*_*nob 915
如果您不在对象中使用函数,则可以使用以下非常简单的衬垫:
const a = {
string: 'string',
number: 123,
bool: false,
nul: null,
date: new Date(), // stringified
undef: undefined, // lost
inf: Infinity, // forced to 'null'
}
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()Run Code Online (Sandbox Code Playgroud)
这适用于包含对象,数组,字符串,布尔值和数字的所有类型的对象.
另请参阅本文关于在向工作人员发送消息和从工作人员发布消息时使用的浏览器的结构化克隆算法.它还包含深度克隆功能.
Pas*_*cal 768
使用jQuery,您可以使用extend进行浅层复制:
var copiedObject = jQuery.extend({}, originalObject)
Run Code Online (Sandbox Code Playgroud)
对copiedObject的后续更改不会影响originalObject,反之亦然.
或者进行深层复制:
var copiedObject = jQuery.extend(true, {}, originalObject)
Run Code Online (Sandbox Code Playgroud)
Vit*_*nko 645
在ECMAScript 6中有Object.assign方法,它将所有可枚举的自有属性的值从一个对象复制到另一个对象.例如:
var x = {myProp: "value"};
var y = Object.assign({}, x);
Run Code Online (Sandbox Code Playgroud)
但请注意,嵌套对象仍会被复制为引用.
itp*_*orn 132
有很多答案,但没有提到ECMAScript 5 中的Object.create,它确实没有给你一个精确的副本,但是将源设置为新对象的原型.
因此,这不是问题的确切答案,但它是一个单行解决方案,因而优雅.它适用于2种情况:
例:
var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property
Run Code Online (Sandbox Code Playgroud)
为什么我认为这个解决方案更优越?它是原生的,因此没有循环,没有递归.但是,旧版浏览器需要填充.
Eug*_*rin 121
一种Object.assign方法是ECMAScript 2015(ES6)标准的一部分,完全符合您的需求.
var clone = Object.assign({}, obj);
Run Code Online (Sandbox Code Playgroud)
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;
}
});
}
Run Code Online (Sandbox Code Playgroud)
Fab*_*oni 83
互联网上的大多数解决方案存在几个问题.所以我决定进行跟进,其中包括为什么接受的答案不应被接受.
我想深入复制一个Javascript Object与它的所有孩子和他们的孩子,等等.但因为我不是那种正常的开发者,我Object有正常的 properties,circular structures甚至是nested objects.
所以让我们创建一个circular structure和nested object第一个.
function Circ() {
this.me = this;
}
function Nested(y) {
this.y = y;
}
Run Code Online (Sandbox Code Playgroud)
让我们把所有东西放在一起Object命名a.
var a = {
x: 'a',
circ: new Circ(),
nested: new Nested('a')
};
Run Code Online (Sandbox Code Playgroud)
接下来,我们要复制a到一个名为变量的变量中b.
var b = a;
b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)
你知道这里发生了什么,因为如果不是你甚至不会落在这个伟大的问题上.
console.log(a, b);
a --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
Run Code Online (Sandbox Code Playgroud)
现在让我们找到一个解决方案.
我尝试的第一次尝试是使用JSON.
var b = JSON.parse( JSON.stringify( a ) );
b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)
不要浪费太多时间,你会得到TypeError: Converting circular structure to JSON.
让我们来看看接受的答案.
function cloneSO(obj) {
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = cloneSO(obj[i]);
}
return copy;
}
// Handle Object
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! Its type isn't supported.");
}
Run Code Online (Sandbox Code Playgroud)
看起来不错,嘿?它是对象的递归副本,也可以处理其他类型Date,但这不是必需的.
var b = cloneSO(a);
b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)
递归并circular structures不能很好地一起工作......RangeError: Maximum call stack size exceeded
在和我的同事争吵之后,我的老板问我们发生了什么,他在谷歌搜索后发现了一个简单的解决方案.它被称为Object.create.
var b = Object.create(a);
b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)
这个解决方案前一段时间被添加到Javascript甚至处理circular structure.
console.log(a, b);
a --> Object {
x: "a",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> Object {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
Run Code Online (Sandbox Code Playgroud)
...而且你看,它不适用于里面的嵌套结构.
Object.create就像IE 8一样,在旧版浏览器中有一个polyfill .它类似于Mozilla推荐的东西,当然,它并不完美,导致与原生解决方案相同的问题.
function F() {};
function clonePF(o) {
F.prototype = o;
return new F();
}
var b = clonePF(a);
b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)
我已经F超出范围,所以我们可以看看instanceof告诉我们的是什么.
console.log(a, b);
a --> Object {
x: "a",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
b --> F {
x: "b",
circ: Circ {
me: Circ { ... }
},
nested: Nested {
y: "b"
}
}
console.log(typeof a, typeof b);
a --> object
b --> object
console.log(a instanceof Object, b instanceof Object);
a --> true
b --> true
console.log(a instanceof F, b instanceof F);
a --> false
b --> true
Run Code Online (Sandbox Code Playgroud)
与本机解决方案相同的问题,但输出稍差.
在挖掘时,我发现了一个类似的问题(在Javascript中,当执行深层复制时,如何避免循环,由于属性是"这个"?)到这个,但有一个更好的解决方案.
function cloneDR(o) {
const gdcc = "__getDeepCircularCopy__";
if (o !== Object(o)) {
return o; // primitive value
}
var set = gdcc in o,
cache = o[gdcc],
result;
if (set && typeof cache == "function") {
return cache();
}
// else
o[gdcc] = function() { return result; }; // overwrite
if (o instanceof Array) {
result = [];
for (var i=0; i<o.length; i++) {
result[i] = cloneDR(o[i]);
}
} else {
result = {};
for (var prop in o)
if (prop != gdcc)
result[prop] = cloneDR(o[prop]);
else if (set)
result[prop] = cloneDR(cache);
}
if (set) {
o[gdcc] = cache; // reset
} else {
delete o[gdcc]; // unset again
}
return result;
}
var b = cloneDR(a);
b.x = 'b';
b.nested.y = 'b';
Run Code Online (Sandbox Code Playgroud)
让我们来看看输出......
console.log(a, b);
a --> Object {
x: "a",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "a"
}
}
b --> Object {
x: "b",
circ: Object {
me: Object { ... }
},
nested: Object {
y: "b"
}
}
console.log(typeof a, typeof b);
a --> object
b --> object
console.log(a instanceof Object, b instanceof Object);
a --> true
b --> true
console.log(a instanceof F, b instanceof F);
a --> false
b --> false
Run Code Online (Sandbox Code Playgroud)
要求相匹配,但仍有一些小问题,包括不断变化instance的nested和circ给Object.
共享叶子的树的结构将不会被复制,它们将成为两个独立的叶子:
[Object] [Object]
/ \ / \
/ \ / \
|/_ _\| |/_ _\|
[Object] [Object] ===> [Object] [Object]
\ / | |
\ / | |
_\| |/_ \|/ \|/
[Object] [Object] [Object]
Run Code Online (Sandbox Code Playgroud)
使用递归和缓存的最后一个解决方案可能不是最好的,但它是对象的真正深层副本.它操作简单properties,circular structures和nested object,但它会弄乱他们的实例,同时克隆.
dul*_*ule 76
如果你对浅拷贝没问题,那么underscore.js库有一个克隆方法.
y = _.clone(x);
Run Code Online (Sandbox Code Playgroud)
或者你可以扩展它
copiedObject = _.extend({},originalObject);
Run Code Online (Sandbox Code Playgroud)
Ali*_*eza 55
好吧,想象一下你有这个对象,你想要克隆它:
let obj = {a:1, b:2, c:3}; //ES6
Run Code Online (Sandbox Code Playgroud)
要么
var obj = {a:1, b:2, c:3}; //ES5
Run Code Online (Sandbox Code Playgroud)
答案主要取决于你使用哪个ECMAscriptES6+,你可以简单地Object.assign用来做克隆:
let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};
Run Code Online (Sandbox Code Playgroud)
或使用像这样的传播运算符:
let cloned = {...obj}; //new {a:1, b:2, c:3};
Run Code Online (Sandbox Code Playgroud)
但是如果你使用ES5,你可以使用很少的方法,但是JSON.stringify,确保你不要使用大量的数据来复制,但在许多情况下它可能是一种方便的方式,如下所示:
let cloned = JSON.parse(JSON.stringify(obj));
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over
Run Code Online (Sandbox Code Playgroud)
Moh*_*fei 49
在 JavaScript 中有三 (3) 种方法可以克隆对象。由于 JavaScript 中的对象是引用值,您不能简单地使用 = 进行复制。
方法是:
const food = { food: 'apple', drink: 'milk' }
// 1. Using the "Spread"
// ------------------
{ ...food }
// 2. Using "Object.assign"
// ------------------
Object.assign({}, food)
// 3. "JSON"
// ------------------
JSON.parse(JSON.stringify(food))
// RESULT:
// { food: 'apple', drink: 'milk' }
Run Code Online (Sandbox Code Playgroud)
希望可以作为参考总结。
Kri*_*ker 40
一个特别不优雅的解决方案是使用JSON编码来制作没有成员方法的对象的深层副本.方法是对您的目标对象进行JSON编码,然后通过对其进行解码,您将获得所需的副本.您可以根据需要尽可能多地进行解码.
当然,函数不属于JSON,因此这仅适用于没有成员方法的对象.
这种方法非常适合我的用例,因为我将JSON blob存储在键值存储中,当它们作为JavaScript API中的对象公开时,每个对象实际上都包含对象原始状态的副本,所以我们可以在调用者突变暴露的对象后计算增量.
var object1 = {key:"value"};
var object2 = object1;
object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);
object2.key = "a change";
console.log(object1);// returns value
Run Code Online (Sandbox Code Playgroud)
mus*_*ind 34
您可以简单地使用spread属性来复制没有引用的对象.但要小心(请参阅注释),'copy'只是在最低的对象/数组级别.嵌套属性仍然是引用!
完整克隆:
let x = {a: 'value1'}
let x2 = {...x}
// => mutate without references:
x2.a = 'value2'
console.log(x.a) // => 'value1'
Run Code Online (Sandbox Code Playgroud)
克隆二级引用:
const y = {a: {b: 'value3'}}
const y2 = {...y}
// => nested object is still a references:
y2.a.b = 'value4'
console.log(y.a.b) // => 'value4'
Run Code Online (Sandbox Code Playgroud)
JavaScript本身实际上不支持深度克隆.使用实用功能.例如Ramda:
Luk*_*nek 25
对于那些使用AngularJS的人来说,还有直接的方法来克隆或扩展这个库中的对象.
var destination = angular.copy(source);
Run Code Online (Sandbox Code Playgroud)
要么
angular.copy(source, destination);
Run Code Online (Sandbox Code Playgroud)
更多angular.copy 文档 ......
Jan*_*roň 22
A.Levy的答案几乎完成,这是我的小贡献:有一种方法如何处理递归引用,看到这一行
if(this[attr]==this) copy[attr] = copy;
如果对象是XML DOM元素,我们必须使用cloneNode代替
if(this.cloneNode) return this.cloneNode(true);
受A.Levy详尽的研究和Calvin的原型设计方法的启发,我提供了这个解决方案:
Object.prototype.clone = function() {
if(this.cloneNode) return this.cloneNode(true);
var copy = this instanceof Array ? [] : {};
for(var attr in this) {
if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
copy[attr] = this[attr];
else if(this[attr]==this) copy[attr] = copy;
else copy[attr] = this[attr].clone();
}
return copy;
}
Date.prototype.clone = function() {
var copy = new Date();
copy.setTime(this.getTime());
return copy;
}
Number.prototype.clone =
Boolean.prototype.clone =
String.prototype.clone = function() {
return this;
}
Run Code Online (Sandbox Code Playgroud)
另见Andy Burke在答案中的注释.
Kam*_*ski 22
今天 2020.04.30 我在 MacOs High Sierra v10.13.6 上的 Chrome v81.0、Safari v13.1 和 Firefox v75.0 上对选定的解决方案进行了测试。
我专注于复制数据的速度(具有简单类型字段的对象,而不是方法等)。解决方案AI只能做浅拷贝,解决方案JU可以做深拷贝。
{...obj}(A) 在 chrome 和 firefox 上速度最快,在 safari 上速度中等Object.assign(B) 的解决方案在所有浏览器上都很快JSON.parse/stringify(K)很慢JSON.parse/stringify(K)很慢对于选择的解决方案: A B C(my) D E F G H I J K L M N O P Q R S T U,我进行了 4 次测试
测试中使用的对象显示在下面的片段中
let obj_ShallowSmall = {
field0: false,
field1: true,
field2: 1,
field3: 0,
field4: null,
field5: [],
field6: {},
field7: "text7",
field8: "text8",
}
let obj_DeepSmall = {
level0: {
level1: {
level2: {
level3: {
level4: {
level5: {
level6: {
level7: {
level8: {
level9: [[[[[[[[[['abc']]]]]]]]]],
}}}}}}}}},
};
let obj_ShallowBig = Array(1000).fill(0).reduce((a,c,i) => (a['field'+i]=getField(i),a) ,{});
let obj_DeepBig = genDeepObject(1000);
// ------------------
// Show objects
// ------------------
console.log('obj_ShallowSmall:',JSON.stringify(obj_ShallowSmall));
console.log('obj_DeepSmall:',JSON.stringify(obj_DeepSmall));
console.log('obj_ShallowBig:',JSON.stringify(obj_ShallowBig));
console.log('obj_DeepBig:',JSON.stringify(obj_DeepBig));
// ------------------
// HELPERS
// ------------------
function getField(k) {
let i=k%10;
if(i==0) return false;
if(i==1) return true;
if(i==2) return k;
if(i==3) return 0;
if(i==4) return null;
if(i==5) return [];
if(i==6) return {};
if(i>=7) return "text"+k;
}
function genDeepObject(N) {
// generate: {level0:{level1:{...levelN: {end:[[[...N-times...['abc']...]]] }}}...}}}
let obj={};
let o=obj;
let arr = [];
let a=arr;
for(let i=0; i<N; i++) {
o['level'+i]={};
o=o['level'+i];
let aa=[];
a.push(aa);
a=aa;
}
a[0]='abc';
o['end']=arr;
return obj;
}Run Code Online (Sandbox Code Playgroud)
下面的片段展示了经过测试的解决方案并显示了它们之间的差异
function A(obj) {
return {...obj}
}
function B(obj) {
return Object.assign({}, obj);
}
function C(obj) {
return Object.keys(obj).reduce( (a,c) => (a[c]=obj[c], a), {})
}
function D(obj) {
let copyOfObject = {};
Object.defineProperties(copyOfObject, Object.getOwnPropertyDescriptors(obj));
return copyOfObject;
}
function E(obj) {
return jQuery.extend({}, obj) // shallow
}
function F(obj) {
return _.clone(obj);
}
function G(obj) {
return _.clone(obj,true);
}
function H(obj) {
return _.extend({},obj);
}
function I(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
function J(obj) {
return _.cloneDeep(obj,true);
}
function K(obj) {
return JSON.parse(JSON.stringify(obj));
}
function L(obj) {
return jQuery.extend(true, {}, obj) // deep
}
function M(obj) {
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = new obj.constructor();
for(var key in obj)
temp[key] = M(obj[key]);
return temp;
}
function N(obj) {
let EClone = function(obj) {
var newObj = (obj instanceof Array) ? [] : {};
for (var i in obj) {
if (i == 'EClone') continue;
if (obj[i] && typeof obj[i] == "object") {
newObj[i] = EClone(obj[i]);
} else newObj[i] = obj[i]
} return newObj;
};
return EClone(obj);
};
function O(obj) {
if (obj == null || typeof obj != "object") return obj;
if (obj.constructor != Object && obj.constructor != Array) return obj;
if (obj.constructor == Date || obj.constructor == RegExp || obj.constructor == Function ||
obj.constructor == String || obj.constructor == Number || obj.constructor == Boolean)
return new obj.constructor(obj);
let to = new obj.constructor();
for (var name in obj)
{
to[name] = typeof to[name] == "undefined" ? O(obj[name], null) : to[name];
}
return to;
}
function P(obj) {
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;
}
return clone({},obj);
}
function Q(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = Q(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = Q(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
function R(obj) {
const gdcc = "__getDeepCircularCopy__";
if (obj !== Object(obj)) {
return obj; // primitive value
}
var set = gdcc in obj,
cache = obj[gdcc],
result;
if (set && typeof cache == "function") {
return cache();
}
// else
obj[gdcc] = function() { return result; }; // overwrite
if (obj instanceof Array) {
result = [];
for (var i=0; i<obj.length; i++) {
result[i] = R(obj[i]);
}
} else {
result = {};
for (var prop in obj)
if (prop != gdcc)
result[prop] = R(obj[prop]);
else if (set)
result[prop] = R(cache);
}
if (set) {
obj[gdcc] = cache; // reset
} else {
delete obj[gdcc]; // unset again
}
return result;
}
function S(obj) {
const cache = new WeakMap(); // Map of old - new references
function copy(object) {
if (typeof object !== 'object' ||
object === null ||
object instanceof HTMLElement
)
return object; // primitive value or HTMLElement
if (object instanceof Date)
return new Date().setTime(object.getTime());
if (object instanceof RegExp)
return new RegExp(object.source, object.flags);
if (cache.has(object))
return cache.get(object);
const result = object instanceof Array ? [] : {};
cache.set(object, result); // store reference to object before the recursive starts
if (object instanceof Array) {
for(const o of object) {
result.push(copy(o));
}
return result;
}
const keys = Object.keys(object);
for (const key of keys)
result[key] = copy(object[key]);
return result;
}
return copy(obj);
}
function T(obj){
var clonedObjectsArray = [];
var originalObjectsArray = []; //used to remove the unique ids when finished
var next_objid = 0;
function objectId(obj) {
if (obj == null) return null;
if (obj.__obj_id == undefined){
obj.__obj_id = next_objid++;
originalObjectsArray[obj.__obj_id] = obj;
}
return obj.__obj_id;
}
function cloneRecursive(obj) {
if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0; i < obj.length; ++i) {
copy[i] = cloneRecursive(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
if (clonedObjectsArray[objectId(obj)] != undefined)
return clonedObjectsArray[objectId(obj)];
var copy;
if (obj instanceof Function)//Handle Function
copy = function(){return obj.apply(this, arguments);};
else
copy = {};
clonedObjectsArray[objectId(obj)] = copy;
for (var attr in obj)
if (attr != "__obj_id" && obj.hasOwnProperty(attr))
copy[attr] = cloneRecursive(obj[attr]);
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
var cloneObj = cloneRecursive(obj);
//remove the unique ids
for (var i = 0; i < originalObjectsArray.length; i++)
{
delete originalObjectsArray[i].__obj_id;
};
return cloneObj;
}
function U(obj) {
/*
Deep copy objects by value rather than by reference,
exception: `Proxy`
*/
const seen = new WeakMap()
return clone(obj)
function defineProp(object, key, descriptor = {}, copyFrom = {}) {
const { configurable: _configurable, writable: _writable }
= Object.getOwnPropertyDescriptor(object, key)
|| { configurable: true, writable: true }
const test = _configurable // Can redefine property
&& (_writable === undefined || _writable) // Can assign to property
if (!test || arguments.length <= 2) return test
const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
|| { configurable: true, writable: true } // Custom…
|| {}; // …or left to native default settings
["get", "set", "value", "writable", "enumerable", "configurable"]
.forEach(attr =>
descriptor[attr] === undefined &&
(descriptor[attr] = basisDesc[attr])
)
const { get, set, value, writable, enumerable, configurable }
= descriptor
return Object.defineProperty(object, key, {
enumerable, configurable, ...get || set
? { get, set } // Accessor descriptor
: { value, writable } // Data descriptor
})
}
function clone(object) {
if (object !== Object(object)) return object /*
—— Check if the object belongs to a primitive data type */
if (object instanceof Node) return object.cloneNode(true) /*
—— Clone DOM trees */
let _object // The clone of object
switch (object.constructor) {
case Array:
case Object:
_object = cloneObject(object)
break
case Date:
_object = new Date(+object)
break
case Function:
const fnStr = String(object)
_object = new Function("return " +
(/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr)
? "function " : ""
) + fnStr
)()
copyPropDescs(_object, object)
break
case RegExp:
_object = new RegExp(object)
break
default:
switch (Object.prototype.toString.call(object.constructor)) {
// // Stem from:
case "[object Function]": // `class`
case "[object Undefined]": // `Object.create(null)`
_object = cloneObject(object)
break
default: // `Proxy`
_object = object
}
}
return _object
}
function cloneObject(object) {
if (seen.has(object)) return seen.get(object) /*
—— Handle recursive references (circular structures) */
const _object = Array.isArray(object)
? []
: Object.create(Object.getPrototypeOf(object)) /*
—— Assign [[Prototype]] for inheritance */
seen.set(object, _object) /*
—— Make `_object` the associative mirror of `object` */
Reflect.ownKeys(object).forEach(key =>
defineProp(_object, key, { value: clone(object[key]) }, object)
)
return _object
}
function copyPropDescs(target, source) {
Object.defineProperties(target,
Object.getOwnPropertyDescriptors(source)
)
}
}
// ------------------------
// Test properties
// ------------------------
console.log(` shallow deep func circ undefined date RegExp bigInt`)
log(A);
log(B);
log(C);
log(D);
log(E);
log(F);
log(G);
log(H);
log(I);
log(J);
log(K);
log(L);
log(M);
log(N);
log(O);
log(P);
log(Q);
log(R);
log(S);
log(T);
log(U);
console.log(` shallow deep func circ undefined date RegExp bigInt
----
LEGEND:
shallow - solution create shallow copy
deep - solution create deep copy
func - solution copy functions
circ - solution can copy object with circular references
undefined - solution copy fields with undefined value
date - solution can copy date
RegExp - solution can copy fields with regular expressions
bigInt - solution can copy BigInt
`)
// ------------------------
// Helper functions
// ------------------------
function deepCompare(obj1,obj2) {
return JSON.stringify(obj1)===JSON.stringify(obj2);
}
function getCase() { // pure data case
return {
undef: undefined,
bool: true, num: 1, str: "txt1",
e1: null, e2: [], e3: {}, e4: 0, e5: false,
arr: [ false, 2, "txt3", null, [], {},
[ true,4,"txt5",null, [], {}, [true,6,"txt7",null,[],{} ],
{bool: true,num: 8, str: "txt9", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
],
{bool: true,num: 10, str: "txt11", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
],
obj: {
bool: true, num: 12, str: "txt13",
e1: null, e2: [], e3: {}, e4: 0, e5: false,
arr: [true,14,"txt15",null,[],{} ],
obj: {
bool: true, num: 16, str: "txt17",
e1: null, e2: [], e3: {}, e4: 0, e5: false,
arr: [true,18,"txt19",null,[],{} ],
obj: {bool: true,num: 20, str: "txt21", e1:null, e2:[] ,e3:{} ,e4: 0, e5: false}
}
}
};
}
function check(org, copy, field, newValue) {
copy[field] = newValue;
return deepCompare(org,copy);
}
function testFunc(f) {
let o = { a:1, fun: (i,j)=> i+j };
let c = f(o);
let val = false
try{
val = c.fun(3,4)==7;
} catch(e) { }
return val;
}
function testCirc(f) {
function Circ() {
this.me = this;
}
var o = {
x: 'a',
circ: new Circ(),
obj_circ: null,
};
o.obj_circ = o;
let val = false;
try{
let c = f(o);
val = (o.obj_circ == o) && (o.circ == o.circ.me);
} catch(e) { }
return val;
}
function testRegExp(f) {
let o = {
re: /a[0-9]+/,
};
let val = false;
try{
let c = f(o);
val = (String(c.re) == String(/a[0-9]+/));
} catch(e) { }
return val;
}
function testDate(f) {
let o = {
date: new Date(),
};
let val = false;
try{
let c = f(o);
val = (+new Date(c.date) == +new Date(o.date));
} catch(e) { }
return val;
}
function testBigInt(f) {
let val = false;
try{
let o = {
big: 123n,
};
let c = f(o);
val = o.big == c.big;
} catch(e) { }
return val;
}
function log(f) {
let o = getCase(); // orginal object
let oB = getCase(); // "backup" used for shallow valid test
let c1 = f(o); // copy 1 for reference
let c2 = f(o); // copy 2 for test shallow values
let c3 = f(o); // copy 3 for test deep values
let is_proper_copy = deepCompare(c1,o); // shoud be true
// shallow changes
let testShallow =
[ ['bool',false],['num',666],['str','xyz'],['arr',[]],['obj',{}] ]
.reduce((acc,curr)=> acc && check(c1,c2,curr[0], curr[1]), true );
// should be true (original object shoud not have changed shallow fields)
let is_valid = deepCompare(o,oB);
// deep test (intruduce some change)
if (c3.arr[6]) c3.arr[6][7].num = 777;
let diff_shallow = !testShallow; // shoud be true (shallow field was copied)
let diff_deep = !deepCompare(c1,c3); // shoud be true (deep field was copied)
let can_copy_functions = testFunc(f);
let can_copy_circular = testCirc(f);
let can_copy_regexp = testRegExp(f);
let can_copy_date = testDate(f);
let can_copy_bigInt = testBigInt(f);
let has_undefined = 'undef' in c1; // field with undefined value is copied?
le
Cal*_*vin 19
从这篇文章:如何在Javascript中复制数组和对象 Brian Huisman:
Object.prototype.clone = function() {
var newObj = (this instanceof Array) ? [] : {};
for (var i in this) {
if (i == 'clone') continue;
if (this[i] && typeof this[i] == "object") {
newObj[i] = this[i].clone();
} else newObj[i] = this[i]
} return newObj;
};
Run Code Online (Sandbox Code Playgroud)
pic*_*rdo 19
这是您可以使用的功能.
function clone(obj) {
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = new obj.constructor();
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
Run Code Online (Sandbox Code Playgroud)
Joã*_*ira 18
在ES-6中,您可以简单地使用Object.assign(...).例如:
let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);
Run Code Online (Sandbox Code Playgroud)
这里有一个很好的参考:https: //googlechrome.github.io/samples/object-assign-es6/
Pav*_*rre 18
在ECMAScript 2018中
let objClone = { ...obj };
Run Code Online (Sandbox Code Playgroud)
请注意,嵌套对象仍会被复制为引用.
Rob*_*ans 13
您可以使用一行代码克隆对象并从前一个引用中删除任何引用.简单地说:
var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references
obj2.text = 'moo2'; // Only updates obj2's text property
console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}
Run Code Online (Sandbox Code Playgroud)
对于当前不支持Object.create的浏览器/引擎,您可以使用此polyfill:
// Polyfill Object.create if it does not exist
if (!Object.create) {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
Run Code Online (Sandbox Code Playgroud)
Cha*_*iam 13
对旧问题的新答案!如果您有幸使用带有Spread语法的 ECMAScript 2016(ES6),那很容易.
keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}
Run Code Online (Sandbox Code Playgroud)
这为对象的浅拷贝提供了一种干净的方法.进行深层复制,意味着在每个递归嵌套对象中设置每个值的新副本,需要上面较重的解决方案.
JavaScript不断发展.
VaZ*_*ZaA 11
使用Lodash:
var y = _.clone(x, true);
Run Code Online (Sandbox Code Playgroud)
flo*_*ori 11
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)
Run Code Online (Sandbox Code Playgroud)
ES6解决方案,如果你想(浅)克隆一个类实例而不只是一个属性对象.
我认为有一个简单而有效的答案.在深度复制中有两个问题:
所以我认为一个简单的解决方案是首先序列化和反序列化,然后对其进行分配以复制函数.
let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);
Run Code Online (Sandbox Code Playgroud)
虽然这个问题有很多答案,但我希望这个问题也有帮助.
小智 8
对于深层复制和克隆,先JSON.stringify然后再JSON.parse对象:
obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}
Run Code Online (Sandbox Code Playgroud)
我在复制对象时遇到问题。这是因为当您执行以下操作时,您只是对对象进行“引用”,并且当稍后更新源对象值时,克隆的复制对象也会更改值,因为它只是一个“引用”,因此您查看源对象最后更改的多个值。
let x = { a: 1 };
let y = x; // y is a reference to x, so if x changes y also changes and v/v
Run Code Online (Sandbox Code Playgroud)
因此,要解决此问题,您需要执行以下操作:
let y = JSON.parse(JSON.stringify(x)); //see Note below
Run Code Online (Sandbox Code Playgroud)
防止引用的另一种方法是执行以下操作:
let x = { a: 1 };
let y = Object.assign({}, x); // Object.assign(target, ...sources)
y.a = 2;
console.log(x); // { a: 1 }
console.log(y); // { a: 2 }
Run Code Online (Sandbox Code Playgroud)
最正确的复制对象是使用Object.create:
Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
Run Code Online (Sandbox Code Playgroud)
这样的符号将使相同的对象具有正确的原型和隐藏属性。
我只想添加到Object.create这篇文章中的所有解决方案,这对于nodejs来说并不是以所需的方式工作.
在Firefox中的结果
var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´
Run Code Online (Sandbox Code Playgroud)
是
{test:"test"}.
在nodejs中
{}
Run Code Online (Sandbox Code Playgroud)
这是A. Levy的代码,同时还处理功能和多/循环引用克隆适应 - 这是什么意思是,如果在树中这两种性能克隆是同一个对象的引用,克隆的对象树将这些属性指向引用对象的同一个克隆.这也解决了循环依赖的情况,如果不处理,会导致无限循环.算法的复杂性是O(n)
function clone(obj){
var clonedObjectsArray = [];
var originalObjectsArray = []; //used to remove the unique ids when finished
var next_objid = 0;
function objectId(obj) {
if (obj == null) return null;
if (obj.__obj_id == undefined){
obj.__obj_id = next_objid++;
originalObjectsArray[obj.__obj_id] = obj;
}
return obj.__obj_id;
}
function cloneRecursive(obj) {
if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;
// Handle Date
if (obj instanceof Date) {
var copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
var copy = [];
for (var i = 0; i < obj.length; ++i) {
copy[i] = cloneRecursive(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
if (clonedObjectsArray[objectId(obj)] != undefined)
return clonedObjectsArray[objectId(obj)];
var copy;
if (obj instanceof Function)//Handle Function
copy = function(){return obj.apply(this, arguments);};
else
copy = {};
clonedObjectsArray[objectId(obj)] = copy;
for (var attr in obj)
if (attr != "__obj_id" && obj.hasOwnProperty(attr))
copy[attr] = cloneRecursive(obj[attr]);
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
var cloneObj = cloneRecursive(obj);
//remove the unique ids
for (var i = 0; i < originalObjectsArray.length; i++)
{
delete originalObjectsArray[i].__obj_id;
};
return cloneObj;
}
Run Code Online (Sandbox Code Playgroud)
一些快速测试
var auxobj = {
prop1 : "prop1 aux val",
prop2 : ["prop2 item1", "prop2 item2"]
};
var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;
obj.f1 = function (){
this.prop1 = "prop1 val changed by f1";
};
objclone = clone(obj);
//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));
objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));
objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
Run Code Online (Sandbox Code Playgroud)
小智 6
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;
};
Run Code Online (Sandbox Code Playgroud)
由于mindeavor声明要克隆的对象是"文字构造的"对象,因此解决方案可能是简单地多次生成对象而不是克隆对象的实例:
function createMyObject()
{
var myObject =
{
...
};
return myObject;
}
var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
Run Code Online (Sandbox Code Playgroud)
小智 6
HTML 标准包括一个内部结构化克隆/序列化算法,可以创建对象的深层克隆。它仍然仅限于某些内置类型,但除了 JSON 支持的少数类型之外,它还支持日期、正则表达式、地图、集合、Blob、文件列表、图像数据、稀疏数组、类型化数组,以及将来可能更多. 它还保留克隆数据中的引用,允许它支持会导致 JSON 错误的循环和递归结构。
v8Node.js 中的模块当前(从 Node 11 开始)直接公开结构化序列化 API,但此功能仍标记为“实验性”,并且在未来版本中可能会更改或删除。如果您使用的是兼容版本,则克隆对象非常简单:
const v8 = require('v8');
const structuredClone = obj => {
return v8.deserialize(v8.serialize(obj));
};
Run Code Online (Sandbox Code Playgroud)
浏览器目前没有为结构化克隆算法提供直接接口,但在 GitHub 上的 whatwg/html#793 中structuredClone()已经讨论了一个全局函数。按照目前的提议,将其用于大多数目的将非常简单:
const clone = structuredClone(original);
Run Code Online (Sandbox Code Playgroud)
除非发布,否则浏览器的结构化克隆实现只会间接暴露。
使用现有 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);
Run Code Online (Sandbox Code Playgroud)
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();
Run Code Online (Sandbox Code Playgroud)
同步创建结构化克隆没有好的选择。这里有一些不切实际的技巧。
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;
};
Run Code Online (Sandbox Code Playgroud)
'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();Run Code Online (Sandbox Code Playgroud)
虽然是同步的,但这可能会非常慢。它会产生与操纵浏览器历史记录相关的所有开销。重复调用此方法可能会导致 Chrome 暂时无响应。
该Notification构造函数创建其相关数据的结构化克隆。它还尝试向用户显示浏览器通知,但除非您请求通知权限,否则这将静默失败。如果您有其他用途的许可,我们将立即关闭我们创建的通知。
const structuredClone = obj => {
const n = new Notification('', {data: obj, silent: true});
n.onshow = n.close.bind(n);
return n.data;
};
Run Code Online (Sandbox Code Playgroud)
'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();Run Code Online (Sandbox Code Playgroud)
小智 6
我写了自己的实现.不确定它是否算作更好的解决方案:
/*
a function for deep cloning objects that contains other nested objects and circular structures.
objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
index (z)
|
|
|
|
|
| depth (x)
|_ _ _ _ _ _ _ _ _ _ _ _
/_/_/_/_/_/_/_/_/_/
/_/_/_/_/_/_/_/_/_/
/_/_/_/_/_/_/...../
/................./
/..... /
/ /
/------------------
object length (y) /
*/
Run Code Online (Sandbox Code Playgroud)
以下是实施:
function deepClone(obj) {
var depth = -1;
var arr = [];
return clone(obj, arr, depth);
}
/**
*
* @param obj source object
* @param arr 3D array to store the references to objects
* @param depth depth of the current object relative to the passed 'obj'
* @returns {*}
*/
function clone(obj, arr, depth){
if (typeof obj !== "object") {
return obj;
}
var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'
var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
if(result instanceof Array){
result.length = length;
}
depth++; // depth is increased because we entered an object here
arr[depth] = []; // this is the x-axis, each index here is the depth
arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
// start the depth at current and go down, cyclic structures won't form on depths more than the current one
for(var x = depth; x >= 0; x--){
// loop only if the array at this depth and length already have elements
if(arr[x][length]){
for(var index = 0; index < arr[x][length].length; index++){
if(obj === arr[x][length][index]){
return obj;
}
}
}
}
arr[depth][length].push(obj); // store the object in the array at the current depth and length
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
原生JS:
const shallowClone = {...originalObj};
const deepClone = JSON.parse(JSON.stringify(originalObj));
Run Code Online (Sandbox Code Playgroud)
使用库:
// Lodash
const shallowClone = _.clone(originalObj);
const deepClone = _. cloneDeep(originalObj);
// JQuery
const shallowClone = jQuery.extend({}, originalObj);
const deepClone = jQuery.extend(true, {}, originalObj);
// Angular
const deepClone = angular.copy(originalObj);
Run Code Online (Sandbox Code Playgroud)
小智 5
JanTotoň的答案非常接近,由于兼容性问题,可能是最好在浏览器中使用,但它可能会导致一些奇怪的枚举问题.例如,执行:
for ( var i in someArray ) { ... }
Run Code Online (Sandbox Code Playgroud)
迭代遍历数组的元素后,将clone()方法赋给i.这是一个避免枚举并适用于node.js的改编:
Object.defineProperty( Object.prototype, "clone", {
value: function() {
if ( this.cloneNode )
{
return this.cloneNode( true );
}
var copy = this instanceof Array ? [] : {};
for( var attr in this )
{
if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
{
copy[ attr ] = this[ attr ];
}
else if ( this[ attr ] == this )
{
copy[ attr ] = copy;
}
else
{
copy[ attr ] = this[ attr ].clone();
}
}
return copy;
}
});
Object.defineProperty( Date.prototype, "clone", {
value: function() {
var copy = new Date();
copy.setTime( this.getTime() );
return copy;
}
});
Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );
Run Code Online (Sandbox Code Playgroud)
这避免了使clone()方法可枚举,因为defineProperty()默认为可枚举为false.
(以下是主要@的整合马切伊·布考斯基,@ A.征,@ 扬TURO?,@ 热度的答案,@ LeviRoberts,@ RobG的意见,非常感谢他们!)
深复制?—是的!(大多);
浅拷贝?—不!(除外Proxy)。
我真诚欢迎大家参加测试clone()。
另外,defineProp()旨在轻松,快速地(重新)定义或复制任何类型的描述符。
"use strict"
function clone(object) {
/*
Deep copy objects by value rather than by reference,
exception: `Proxy`
*/
const seen = new WeakMap()
return clone(object)
function clone(object) {
if (object !== Object(object)) return object /*
—— Check if the object belongs to a primitive data type */
if (object instanceof Node) return object.cloneNode(true) /*
—— Clone DOM trees */
let _object // The clone of object
switch (object.constructor) {
case Array:
case Object:
_object = cloneObject(object)
break
case Date:
_object = new Date(+object)
break
case Function:
const fnStr = String(object)
_object = new Function("return " +
(/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr)
? "function " : ""
) + fnStr
)()
copyPropDescs(_object, object)
break
case RegExp:
_object = new RegExp(object)
break
default:
switch (Object.prototype.toString.call(object.constructor)) {
// // Stem from:
case "[object Function]": // `class`
case "[object Undefined]": // `Object.create(null)`
_object = cloneObject(object)
break
default: // `Proxy`
_object = object
}
}
return _object
}
function cloneObject(object) {
if (seen.has(object)) return seen.get(object) /*
—— Handle recursive references (circular structures) */
const _object = Array.isArray(object)
? []
: Object.create(Object.getPrototypeOf(object)) /*
—— Assign [[Prototype]] for inheritance */
seen.set(object, _object) /*
—— Make `_object` the associative mirror of `object` */
Reflect.ownKeys(object).forEach(key =>
defineProp(_object, key, { value: clone(object[key]) }, object)
)
return _object
}
function copyPropDescs(target, source) {
Object.defineProperties(target,
Object.getOwnPropertyDescriptors(source)
)
}
}
function defineProp(object, key, descriptor = {}, copyFrom = {}) {
const { configurable: _configurable, writable: _writable }
= Object.getOwnPropertyDescriptor(object, key)
|| { configurable: true, writable: true }
const test = _configurable // Can redefine property
&& (_writable === undefined || _writable) // Can assign to property
if (!test || arguments.length <= 2) return test
const basisDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
|| { configurable: true, writable: true } // Custom…
|| {}; // …or left to native default settings
["get", "set", "value", "writable", "enumerable", "configurable"]
.forEach(attr =>
descriptor[attr] === undefined &&
(descriptor[attr] = basisDesc[attr])
)
const { get, set, value, writable, enumerable, configurable }
= descriptor
return Object.defineProperty(object, key, {
enumerable, configurable, ...get || set
? { get, set } // Accessor descriptor
: { value, writable } // Data descriptor
})
}
Run Code Online (Sandbox Code Playgroud)
"use strict"
const obj0 = {
u: undefined,
nul: null,
t: true,
num: 9,
str: "",
sym: Symbol("symbol"),
[Symbol("e")]: Math.E,
arr: [[0], [1, 2]],
d: new Date(),
re: /f/g,
get g() { return 0 },
o: {
n: 0,
o: { f: function (...args) { } }
},
f: {
getAccessorStr(object) {
return []
.concat(...
Object.values(Object.getOwnPropertyDescriptors(object))
.filter(desc => desc.writable === undefined)
.map(desc => Object.values(desc))
)
.filter(prop => typeof prop === "function")
.map(String)
},
f0: function f0() { },
f1: function () { },
f2: a => a / (a + 1),
f3: () => 0,
f4(params) { return param => param + params },
f5: (a, b) => ({ c = 0 } = {}) => a + b + c
}
}
defineProp(obj0, "s", { set(v) { this._s = v } })
defineProp(obj0.arr, "tint", { value: { is: "non-enumerable" } })
obj0.arr[0].name = "nested array"
let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", { set(v) { this._s = v + 1 } })
defineProp(obj1.re, "multiline", { value: true })
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Routinely")
console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()
console.log("obj0\n ",
".arr.tint:", obj0.arr.tint, "\n ",
".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
".arr.tint:", obj1.arr.tint, "\n ",
".arr[0].name:", obj1.arr[0].name
)
console.log()
console.log("Accessor-type descriptor\n ",
"of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
"of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
"set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
" ? (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)
console.log("—— obj0 has not been interfered.")
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Circular structures")
obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr
obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log("Clear obj0's recursion:",
obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
"obj0\n ",
".o.r:", obj0.o.r, "\n ",
".arr:", obj0.arr
)
console.log(
"obj1\n ",
".o.r:", obj1.o.r, "\n ",
".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")
console.log("\n\n" + "-".repeat(2 ** 6))
console.log(">:>: Test - Classes")
class Person {
constructor(name) {
this.name = name
}
}
class Boy extends Person { }
Boy.prototype.sex = "M"
const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }
const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"
boy0.name = "one"
boy1.name = "neo"
console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)
Run Code Online (Sandbox Code Playgroud)
浅表复制:lodash _.clone()
可以通过简单地复制参考来进行浅表复制。
let obj1 = {
a: 0,
b: {
c: 0,
e: {
f: 0
}
}
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;
console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}
console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}
Run Code Online (Sandbox Code Playgroud)
深度复制:lodash _.cloneDeep()
字段被取消引用:而不是引用要复制的对象
let obj1 = {
a: 0,
b: {
c: 0,
e: {
f: 0
}
}
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;
console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}
console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}
Run Code Online (Sandbox Code Playgroud)
对象复制使用 ( ... )
//bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2,c: 3 }
//good
const originalObj = { id: 5, name: 'San Francisco'};
const copyObject = {...originalObj, pincode: 4444};
console.log(copyObject) //{ id: 5, name: 'San Francisco', pincode: 4444 }
Run Code Online (Sandbox Code Playgroud)
同样可用于将数组从一个复制到另一个
const itemsCopy = [...items];
Run Code Online (Sandbox Code Playgroud)
小智 5
克隆对象的简单递归方法。也可以使用 lodash.clone。
let clone = (obj) => {
let obj2 = Array.isArray(obj) ? [] : {};
for(let k in obj) {
obj2[k] = (typeof obj[k] === 'object' ) ? clone(obj[k]) : obj[k];
}
return obj2;
}
let w = { name: "Apple", types: ["Fuji", "Gala"]};
let x = clone(w);
w.name = "Orange";
w.types = ["Navel"];
console.log(x);
console.log(w);Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1756071 次 |
| 最近记录: |