🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# JS深浅拷贝原理 [blog](https://www.jianshu.com/p/ce89606aa90c) ## 总结 基本数据类型---名字和值都会储存在栈内存中 引用数据类型--- 名字存在栈内存中,值存在堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值 当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值 - 浅拷贝 for···in只循环第一层; Object.assign方法; 直接用=赋值 - 深拷贝 采用递归去拷贝所有层级属性;JSON.parse(JSON.stringify()); slice实现对数组的深拷贝(数组里面的值是基本数据类型); ```js function deepClone(obj){ let objClone = Array.isArray(obj) ? [] : {}; if(obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ //判断ojb子元素是否为对象 if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); } else{ objClone[key] = obj[key]; } //如果不是,简单复制 } } } return objClone; } ``` ------ ------- -------- ------- 浅拷贝:浅拷贝通过ES6新特性Object.assign()或者通过扩展运算法...来达到浅拷贝的目的,浅拷贝修改 副本,不会影响原数据,但缺点是浅拷贝只能拷贝第一层的数据,且都是值类型数据,如果有引用型数据,修改副本会影响原数据。 深拷贝:通过利用JSON.parse(JSON.stringify())来实现深拷贝的目的,但利用JSON拷贝也是有缺点的, 当要拷贝的数据中含有undefined/function/symbol类型是无法进行拷贝的,当然我们想项目开发中需要 深拷贝的数据一般不会含有以上三种类型,如有需要可以自己在封装一个函数来实现。 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。浅拷贝只复制对象的第一层属性 但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。对对象的属性进行递归复制 ###### 浅拷贝 实现方法 1、简单的赋值实现—— 针对引用类型 数组、对象、函数 ```javascript let arr = [22, 44, 66, 88]; let co = arr; co[0] = 11; console.log(arr, co); // 11, 44, 66, 88 11, 44, 66, 88 ``` >原理: 对于引用类型,赋值操作符只是把存放在栈内容中的指针赋值给另外一个变量。 所以在赋值完成后,在栈内存就有两个指针指向堆内存同一个数据。 也就可以说两个变量在共用着同一个数据,这就是浅拷贝。 2、Object.assign()实现 Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。 ```javascript var obj = { a: {a: "hello", b: 21} }; var initalObj = Object.assign({}, obj); initalObj.a.a = "changed"; console.log(obj.a.a); // "changed" ----------------------------------------------------- // 注意:当object只有一层的时候,是深拷贝,例如如下: var obj1 = { a: 10, b: 20, c: 30 }; var obj2 = Object.assign({}, obj1); obj2.b = 100; console.log(obj1); // { a: 10, b: 20, c: 30 } <-- 沒被改到 console.log(obj2); // { a: 10, b: 100, c: 30 } ``` 封装浅拷贝方法 ```javascript var easyCopy = function ( extendObj ) { var newObj = extendObj.constructor === Array ? [] : {}; if (typeof extendObj != 'object') return; for (var key in extendObj) { if (extendObj.hasOwnProperty(key)) { // Object的hasOwnProperty():判断对象是否包含特定的自身(非继承)属性。 newObj[key] = extendObj[key]; } } return newObj }; var obj2 = { tall: 1.8, weight: 75 } var obj1 = easyCopy( obj2 ); console.log( obj1 ); ``` ###### 深拷贝 实现方法 深拷贝其实就是将对象中的数组、子对象进行深度递归遍历,直到其不是引用类型位置再进行复制,这样即使改变了其中一个的值,也不会影响到另一个 深拷贝的实现方式 1、对象只有一层的话可以使用上面的:Object.assign()函数 2、转成 JSON 再转回来 用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。 ```javascript var obj1 = { body: { a: 10 } }; var obj2 = JSON.parse(JSON.stringify(obj1)); ``` 深拷贝的封装 ```javascript var deepCopy = function( extendObj ){ var str, newObj = extendObj.constructor === Array ? [] : {}; if(typeof extendObj !== 'object'){ return; } else if(window.JSON){ str = JSON.stringify(extendObj); newObj = JSON.parse(str); } else { for(var key in extendObj){ // for … in 遍历任何一个对象,所有属性全部遍历出来,包括原型链上的属性 if (!extendObj.hasOwnProperty(key)) return; newObj[key] = typeof extendObj[key] === 'object' ? cloneObj(extendObj[key]) : extendObj[key]; } } return newObj; }; var obj2 = { names: ['test0', 'test1', 'test3'] } var obj1 = deepCopy( obj2 ); console.log( obj1, obj2 ); obj2.names[1] = 'test0'; console.log( obj1, obj2 ); ``` 3、递归拷贝 ```javascript // 定义一个深拷贝函数 接收目标target参数 function deepClone(target) { // 定义一个变量 let result; // 如果当前需要深拷贝的是一个对象的话 if (typeof target === 'object') { // 如果是一个数组的话 if (Array.isArray(target)) { result = []; // 将result赋值为一个数组,并且执行遍历 for (let i in target) { // 递归克隆数组中的每一项 result.push(deepClone(target[i])) } // 判断如果当前的值是null的话;直接赋值为null } else if(target===null) { result = null; // 判断如果当前的值是一个RegExp对象的话,直接赋值 } else if(target.constructor===RegExp){ result = target; }else { // 否则是普通对象,直接for in循环,递归赋值对象的所有值 result = {}; for (let i in target) { result[i] = deepClone(target[i]); } } // 如果不是对象的话,就是基本数据类型,那么直接赋值 } else { result = target; } // 返回最终结果 return result; } var str = {}; var obj = { a: {a: "hello", b: 21} }; deepClone(obj, str); ``` 4、使用Object.create()方法 直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。 ```javascript function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj; } ``` 5、jquery提供一个$.extend可以用来做 Deep Copy。 ```javascript var $ = require('jquery'); var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var obj2 = $.extend(true, {}, obj1); console.log(obj1.b.f === obj2.b.f); // false ``` 6、函数库lodash,也有提供_.cloneDeep用来做 Deep Copy。 ```javascript var _ = require('lodash'); var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var obj2 = _.cloneDeep(obj1); console.log(obj1.b.f === obj2.b.f); // false ```