/* ===================== 直接看代碼 ===================== */
<!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 = -1; 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 (遞歸方法總結的很全面推薦)
