Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

赋值,深拷贝,浅拷贝区别 #4

Open
yrmatou opened this issue Apr 1, 2021 · 0 comments
Open

赋值,深拷贝,浅拷贝区别 #4

yrmatou opened this issue Apr 1, 2021 · 0 comments

Comments

@yrmatou
Copy link
Owner

yrmatou commented Apr 1, 2021

1.参考文章 2.参考文章-推荐 3.参考文章

image

拓展

堆和栈的区别

其实深拷贝和浅拷贝的主要区别就是其在内存中的存储类型不同。
堆和栈都是内存中划分出来用来存储的区域。
栈(stack)为自动分配的内存空间,它由系统自动释放;
而堆(heap)则是动态分配的内存,大小不定也不会自动释放。

数据类型

基本数据类型有五种:number、string、boolean、null、undefined。基本数据类型存放在栈中。
存放在栈中的数据具有数据大小确定,内存空间大小可以分配、直接按值存放的特点。
所以存放在栈中的数据可以直接访问。在JavaScript中,基本的数据类型值是不可更改的。

引用数据类型:是存放在堆内存中。引用数据类型的变量并不是存放的实际值,
而是一个存放在栈内存的指针,该指针指向堆内存中的某个地址。每个数据所占的空间大小不一致,
需要根据情况进行特定的分配。与基本数据类型不同,引用类型的值是可以改变的。

赋值

赋值是将某一数值或对象赋给某个变量的过程,分两种情况:

  • 基本数据类型:赋值,赋值后两个变量互不影响
  • 引用数据类型: 赋址,两个变量指向同一个地址,同一个对象,相互之间有影响
  对基本数据类型的赋值,两个变量相互不影响:
    var a = 1;
    var b = a;
    a = 2;
    console.log(a); // 2
    console.log(b); // 1
    复制代码对引用数据类型进行赋址操作,两个变量指向同一个对象,
    改变变量a的值会影响变量b的值,哪怕改变的只是对象a中的基础数据类型:
    var a = {
        name: 'Jane',
        book: {
            name: 'Vue.js',
            price: 50
        }
    };
    var b = a;
    b.name = 'hahaha';
    b.book.price = 52;
    console.log(a); // { name: 'hahaha', book: { name: 'Vue.js', price: 52 } }
    console.log(b); // { name: 'hahaha', book: { name: 'Vue.js', price: 52 } }
    复制代码通常开发中我们不希望出现这种相互影响的情况,所以需要浅拷贝或者深拷贝。

浅拷贝

重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,只拷贝一层,不能对对象中的子对象进行
拷贝
如果属性是基本数据类型,拷贝的是基本数据类型的值;如果属性是引用类型,拷贝的是内存地址,
所以如果一个对象改变了这个地址,就会影响到另外一个对象。

简单说,浅拷贝只解决了第一层的问题,即第一层的基本数据类型和引用类型数据的引用地址

  赋值示例:
  var a = {a:2,b:2}
  var b = a
  undefined
  b.a = 3
  // a {a: 3, b: 2}
  // b {a: 3, b: 2}
  浅拷贝示例:
    var ac = {a: 2, b:3, obj: {a1:2},arr: [1,2,3]}
    var c = cloneShallow(ac) // 浅拷贝函数
    // c 等于 {a: 2, b: 3, obj: {a1:2}, arr: [1,2,3]}
    c.a = 1
    // ac.a = 2
    // 结论是 普通属性不会改变
    c.obj.a1 = 3
    // ac.obj.a1 = 3
    // 结论是 引用属性指向同一个地址 会改变
浅拷贝几种方法:
  • Object.assign()
    Object.assign()把所有可枚举属性从一个或多个对象复制到目标对象,返回目标对象
   var a = {
      name: 'Jane',
      book: {
          name: 'Vue.js',
          price: 50
      }
  };
  var b = Object.assign({}, a);
  b.name = 'hahaha';
  b.book.price = 52;
  console.log(a); // { name: 'Jane', book: { name: 'Vue.js', price: 52 } }
  console.log(b); // { name: 'hahaha', book: { name: 'Vue.js', price: 52 } }
  • ES6拓展符 ...
  var b = {...a};
  b.name = 'hahaha';
  b.book.price = 52;
  console.log(a); // { name: 'Jane', book: { name: 'Vue.js', price: 52 } }
  console.log(b); // { name: 'hahaha', book: { name: 'Vue.js', price: 52 } }
  • Array.prototype.slice()
  slice()方法返回一个新的数组对象,这个对象是由begin和end(不包括end)决定的原数组的浅拷贝。原数组不会被改变。
  var a = [0, '1', [2, 3]];
  var b = a.slice(1);
  console.log(b); // ['1', [2, 3]]
  a[1] = '99';
  a[2][0] = 4;
  console.log(a); // [0, '99', [4, 3]]
  console.log(b); // ['1', [4, 3]]
  复制代码从上面的代码可以看出,a[1]改变之后b[0]并没有改变,a[2][0]改变之后相应的b[1][0]跟着改变了。
  说明slice()是浅拷贝,相应的还有concat等。
  • 常用的循环方法
  function cloneShallow(source) {
    var target = {};
    for (var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            target[key] = source[key];
        }
    }
    return target;
  }

深拷贝

深拷贝是对对象以及对象的所有子对象进行拷贝,浅拷贝的进阶版
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。
当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相较于浅拷贝速度慢并且花销更大。
深拷贝的两个对象互不影响。

  • JSON.parse(JSON.stringify())
    缺点:这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,
    因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),
    得到的函数就不再是函数(变
    为null)了。
    image
  • 主流解决方法
   1.es6方法

     function isObject(x) {
        return Object.prototype.toString.call(x) === '[object Object]';
     }

     function cloneDeep4(source, hash = new WeakMap()) {
  
        if (!isObject(source)) return source; 
        if (hash.has(source)) return hash.get(source); 
          
        let target = Array.isArray(source) ? [...source] : { ...source }; // 改动 1
        hash.set(source, target);
        
        Reflect.ownKeys(target).forEach(key => { // 改动 2
           if (isObject(source[key])) {
                target[key] = cloneDeep4(source[key], hash); 
            } else {
                target[key] = source[key];
            }  
        });
         return target;
      }

手工实现

loadsh解决更多的复杂判断

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant