飞道的博客

深浅拷贝的区别?如何实现一个深拷贝?

362人阅读  评论(0)

一.数据类型存储

js中存在两大数据类型:

基本数据类型:保存在栈内存中;

引用数据类型:保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中。

二.浅拷贝

浅拷贝:指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值。如果属性的值是引用类型,拷贝的就是内存地址。

来看一个浅拷贝的例子:


  
  1. <script>
  2. function shallowClone( object) {
  3. const newObj = {}
  4. for ( const key in object) {
  5. if ( Object. hasOwnProperty(object)) {
  6. newObj[key] = object[key];
  7. }
  8. }
  9. return newObj
  10. }
  11. </script>

在js中,存在浅拷贝的现象有以下几种,分别在一一介绍,并在每种方法后加一个小例子以供参考:

1.Object.assign


  
  1. <script>
  2. let obj = {
  3. name: '孙兴慜',
  4. index: [ '1', '2'],
  5. hobby: {
  6. sport: "football",
  7. },
  8. run: function ( ) {
  9. console. log( '了不起的足球运动员');
  10. }
  11. }
  12. let newObj = Object. assign({}, obj)
  13. console. log(newObj);
  14. console. log(obj);
  15. </script>

2.concat()


  
  1. <script>
  2. const arr=[ '1', '2', '3']
  3. const newaArr=arr. concat()
  4. arr[ 1]= '100'
  5. console. log(arr); //['1','100','3']
  6. console. log(newaArr); //['1','2','3']
  7. </script>

3.扩展运算符


  
  1. <script>
  2. const arr=[ '1', '2', '3']
  3. const newaArr=[...arr]
  4. arr[ 1]= '100'
  5. console. log(arr); //['1','100','3']
  6. console. log(newaArr); //['1','2','3']
  7. </script>

4.slice()


  
  1. <script>
  2. const arr = [ '1', '2', '3']
  3. const newaArr = arr. slice( 0)
  4. arr[ 1] = '100'
  5. console. log(arr); //['1','100','3']
  6. console. log(newaArr); //['1','2','3']
  7. </script>

3.深拷贝

深拷贝开辟一个新的栈,两个对象属性完全相同,但是对应两个不同的地址,因此修改一个对象的属性,不会改变另一个对象的属性。

下面一一介绍深拷贝的方式,并一一举例说明:

(1)_.cloneDeep()


  
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document </title>
  8. <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.min.js"> </script>
  9. </head>
  10. <body>
  11. <script>
  12. let objects = {
  13. name: '胡歌',
  14. score: [ '10', '20', '30'],
  15. hobby: {
  16. sing: '忘记时间',
  17. index: 1
  18. }
  19. }
  20. let deep = _. cloneDeep(objects); //_.cloneDeep其实是lodash库的一个深拷贝的方法
  21. objects. score. push( '50') //验证一下深拷贝有没有拷贝下来数据
  22. console. log(objects);
  23. console. log(deep);
  24. </script>
  25. </body>
  26. </html>

看下控制台输出:

 (2)jQuery.extend()


  
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document </title>
  8. <!-- 我是通过MDN方式引入的jquery -->
  9. <script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.js"> </script>
  10. </head>
  11. <body>
  12. <script>
  13. const obj1 = {
  14. a: 1,
  15. b: { c: { g: 2 } },
  16. h: [ 1, 2, 3]
  17. }
  18. const obj2 = jQuery. extend( true, {}, obj1)
  19. console. log(obj1. b. c === obj2. b. c); //false 对应两个不同的地址,他们并不相等
  20. </script>
  21. </body>
  22. </html>

(3)JSON.stringify()


  
  1. <script>
  2. const obj1 = {
  3. a: 1,
  4. b: { c: { g: 2 } },
  5. h: [ 1, 2, 3]
  6. }
  7. const obj2 = JSON. parse( JSON. stringify(obj1))
  8. console. log(obj1. b. c === obj2. b. c); //false 对应两个不同的地址,他们并不相等
  9. </script>

但是通过JSON.stringify)这种实现深拷贝的方式存在弊端,比如说会忽略undefined,Symbol和函数。来看例子:


  
  1. <script>
  2. const obj1 = {
  3. a: 1, //会拷贝下来
  4. b: undefined, //不会被拷贝
  5. c: function( ){}, //不会被拷贝
  6. d: Symbol( 'A') //不会被拷贝
  7. }
  8. const obj2= JSON. parse( JSON. stringify(obj1))
  9. console. log(obj2); //{a:1}
  10. </script>

看下运行结果:

(4)循环递归


  
  1. <script>
  2. //这里先简单说一下WeakMap(弱映射)
  3. //WeakMap(弱映射)的主要用途是实现值与对象的关联而不导致内存泄漏
  4. //WeakMap(弱映射)其实是Map类的一个变体,并不是子类
  5. //WeakMap的键必须是对象或者数组,原始值不受垃圾收集控制,不能作为键
  6. //WeakMap只实现了get(),set(),has(),delete()方法,特别是,WeakMap不是可迭代对象
  7. //所以没有定义keys(),values()和forEach()方法
  8. function deepClone( obj, hash = new WeakMap()) {
  9. if (obj === null) return obj; //如果是null,就不进行深拷贝操作
  10. if (obj instanceof Date) return new Date(obj)
  11. if (obj instanceof RegExp) return new RegExp(obj)
  12. //这里可能是对象或普通的值,如果是函数的话就不需要深拷贝了
  13. if ( typeof obj !== 'object') return obj;
  14. //是对象的话就进行深拷贝
  15. if (hash. get(obj)) return hash. get(obj);
  16. let cloneObj = new obj. constructor( )
  17. //找到的是所属类原型上的constructor,而原型上的constructor指向的是当前类本身
  18. hash. set(obj, cloneObj);
  19. for ( const key in obj) {
  20. if (obj. hasOwnProperty(key)) {
  21. cloneObj[key] = deepClone(object[key], hash);
  22. }
  23. }
  24. return cloneObj;
  25. }
  26. </script>

 4.深浅拷贝的区别

借助一张图来看下两者区别在哪里:

 可以发现,浅拷贝和深拷贝都创建出一个新的对象,但在复制对象属性的时候,行为就不一样。浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,因此修改对象的属性会影响原对象。

举例说明:


  
  1. <script>
  2. //浅拷贝
  3. const obj={
  4. name: '王源',
  5. arr:[ 1,[ 2, 3], 4]
  6. }
  7. const newObj= Object. assign(obj)
  8. newObj. name= '易烊千玺'
  9. newObj. arr[ 1]=[ 5, 6, 7] //新旧对象还是共享同一块内存
  10. console. log( 'obj',obj);
  11. console. log( 'newObj',newObj);
  12. </script>

看下输出结果:

而深拷贝会另外创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会影响原对象。举例说明:


  
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>Document </title>
  8. <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.20/lodash.min.js"> </script>
  9. </head>
  10. <body>
  11. <script>
  12. //深拷贝
  13. const obj = {
  14. name: '王源',
  15. arr: [ 1, [ 2, 3], 4]
  16. }
  17. const newObj = _. cloneDeep(obj)
  18. newObj. name = '易烊千玺'
  19. newObj. arr[ 1] = [ 5, 6, 7] //新旧对象还是共享同一块内存
  20. console. log( 'obj', obj);
  21. console. log( 'newObj', newObj);
  22. </script>
  23. </body>
  24. </html>

 看下输出结果:

总结:

前提是拷贝类型是引用类型的情况下:

(1)浅拷贝只拷贝一层,属性为对象时,浅拷贝时复制,两个对象指向同一个地址;

(2)深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开辟栈,两个对象指向不同的地址。 


转载:https://blog.csdn.net/huihui_999/article/details/128152961
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场