/* ===================== 直接看代码 ===================== */
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>http://www.codeceo.com/article/javascript-object-deep-copy.html</h1>
<p>
对象的深拷贝与浅拷贝的区别如下:
浅拷贝:仅仅复制对象的引用,而不是对象本身;
深拷贝:把复制的对象所引用的全部对象都复制一遍。
</p>
一. 浅拷贝的实现
<script type="text/javascript">Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象 (简单粗暴明了 推荐首选)
详情直戳 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign。
function test() {
'use strict'; let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}} obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}} obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}} obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}} // Deep Clone
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}} // 拷贝单个对象
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 } // 合并多个对象
let o1 = { a: 1 };
let o2 = { b: 2 };
let o3 = { c: 3 }; let obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变
} // 需要注意的是浅拷贝的话当自身或者目标对象改变两者皆会改变 (希望不改变则需要深拷贝)
test();
</script>
<script type="text/javascript">
二. 深拷贝的实现
要实现深拷贝有很多办法,有最简单的 JSON.parse() 方法,也有常用的递归拷贝方法,和ES5中的 Object.create() 方法。
2.1 方法一:使用 JSON.parse() 方法
要实现深拷贝有很多办法,比如最简单的办法是使用 JSON.parse():
function deepClone(initalObj) {
var obj = {};
try {
obj = JSON.parse(JSON.stringify(initalObj));
}
return obj;
}
var obj = {
a: {
a: "world",
b: 21
}
}
var cloneObj = deepClone(obj);
cloneObj.a.a = "changed";
console.log(obj.a.a); // "world"
这种方法简单易用。
但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。
2.2 方法二:递归拷贝
为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。
版代码如下:
// 深拷贝 递归1.1 写法一
function deepClone(obj) {
var obj = {};
for (var i in initalObj) {
var prop = initalObj[i];
// 避免相互引用对象导致死循环,
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
// 深拷贝 递归1.2 写法二
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}
// 深拷贝 递归1.3 写法二 比较全面的写法 参考至 (https://mp.weixin.qq.com/s/vXbFsG59L1Ba0DMcZeU2Bg)
function forEach(array, cloneTarget) {
let index = -;
const length = array.length;
while (++index < length) {
cloneTarget(array[index], index);
}
return array;
} function clone(target, map = new WeakMap()) {
if (typeof target === 'object') {
const isArray = Array.isArray(target);
let cloneTarget = isArray ? [] : {};
if (map.get(target)) {
return target;
}
map.set(target, cloneTarget);
const keys = isArray ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) key = value
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
} else {
return target;
}
}
2.3 方法三:集合Object.assign()方法
// 深拷贝
function deepClone(initalObj) {
var obj = {};
for (var i in initalObj) {
var prop = initalObj[i];
// 避免相互引用对象导致死循环
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = Object.assign(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
</script>
</body>
</html>
有问题或者有bug非常欢迎留言指正。
深拷贝1.3版本参考至: https://mp.weixin.qq.com/s/vXbFsG59L1Ba0DMcZeU2Bg (递归方法总结的很全面推荐)