zoukankan      html  css  js  c++  java
  • js 浅拷贝和深拷贝

    传值与传址

    了解了基本数据类型与引用类型的区别之后,我们就应该能明白传值与传址的区别了。
    在我们进行赋值操作的时候,基本数据类型的赋值(=)是在内存中新开辟一段栈内存,然后再把再将值赋值到新的栈中。例如:

    var a = 10;
    var b = a;
    
    a ++ ;
    console.log(a); // 11
    console.log(b); // 10

    所以说,基本类型的赋值的两个变量是两个独立相互不影响的变量。

    但是引用类型的赋值是传址。只是改变指针的指向,例如,也就是说引用类型的赋值是对象保存在栈中的地址的赋值,这样的话两个变量就指向同一个对象,因此两者之间操作互相有影响。例如:

    var a = {}; // a保存了一个空对象的实例
    var b = a;  // a和b都指向了这个空对象
    
    a.name = 'jozo';
    console.log(a.name); // 'jozo'
    console.log(b.name); // 'jozo'
    
    b.age = 22;
    console.log(b.age);// 22
    console.log(a.age);// 22
    
    console.log(a == b);// true

    浅拷贝

    先来看一段代码的执行:

    var obj = {a: 1, b: {c: 2}}
    var obj1 = obj
    var obj2 = shallowCopy(obj);
    function shallowCopy(src) {
        var dst = {};
         for (var prop in src) {
             if (src.hasOwnProperty(prop)) {
                 dst[prop] = src[prop];
              }
          }
         return dst;
    }
    
    var obj3 = Object.assign({}, obj)
    
    obj.a = 2
    obj.b.c = 3
    
    console.log(obj) // {a: 2, b: {c: 3}}
    console.log(obj1) // {a: 2, b: {c: 3}}
    console.log(obj2) // {a: 1, b: {c: 3}}
    console.log(obj3) // {a: 1, b: {c: 3}}

    这段代码可以说明赋值得到的对象 obj1 只是将指针改变,其引用的仍然是同一个对象,而浅拷贝得到的的 obj2 则是重新创建了新对象。但是,如果原对象obj中存在另一个对象,则不会对对象做另一次拷贝,而是只复制其变量对象的地址。这是因为浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。
    对于数组,更长见的浅拷贝方法便是slice(0)和 concat()
    ES6 比较常见的浅拷贝方法便是 Object.assign

    深拷贝

    通过上面的这些说明,相信你对深拷贝大致了解了是怎样一个东西了:深拷贝是对对象以及对象的所有子对象进行拷贝。那么如何实现这样一个深拷贝呢?

    1. JSON.parse(JSON.stringify(obj))

    对于常规的对象,我们可以通过JSON.stringify来讲对象转成一个字符串,然后在用JSON.parse来为其分配另一个存储地址,这样可以解决内存地址指向同一个的问题:

    var obj = {a: {b: 1}}
    var copy = JSON.parse(JSON.stringify(obj))
    
    obj.a.b = 2
    console.log(obj) // {a: {b: 2}}
    console.log(copy) // {a: {b: 1}}

    但是 JSON.parse()JSON.stringify也存在一个问题,JSON.parse()和J SON.stringify()能正确处理的对象只有Number、String、Array等能够被 json 表示的数据结构,因此函数这种不能被 json 表示的类型将不能被正确处理。

    var target = {
        a: 1,
        b: 2,
        hello: function() { 
                console.log("Hello, world!");
        }
    };
    var copy = JSON.parse(JSON.stringify(target));
    console.log(copy);   // {a: 1, b: 2}
    console.log(JSON.stringify(target)); // "{"a":1,"b":2}"

    2. 遍历实现属性复制

    既然浅拷贝只能实现非object第一层属性的复制,那么遇到object只需要通过递归实现浅拷贝其中内部的属性即可:

    function extend (source) {
      var target
      if (typeof source === 'object') {
        target = Array.isArray(source) ? [] : {}
        for (var key in source) {
          if (source.hasOwnProperty(key)) {
            if (typeof source[key] !== 'object') {
              target[key] = source[key]
            } else {
              target[key] = extend(source[key])
            }
          }
        }
      } else {
        target = source
      }
      return target
    }
    
    var obj1 = {a: {b: 1}}
    var cpObj1 = extend(obj1)
    obj1.a.b = 2
    console.log(cpObj1) // {a: {b: 1}}
    
    var obj2 = [[1]]
    var cpObj2 = extend(obj2) 
    obj2[0][0] = 2
    console.log(cpObj2) // [[1]]

    我们再来看一下 Zepto 中深拷贝的代码:

        // 内部方法:用户合并一个或多个对象到第一个对象
        // 参数:
        // target 目标对象  对象都合并到target里
        // source 合并对象
        // deep 是否执行深度合并
        function extend(target, source, deep) {
            for (key in source)
                if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
                    // source[key] 是对象,而 target[key] 不是对象, 则 target[key] = {} 初始化一下,否则递归会出错的
                    if (isPlainObject(source[key]) && !isPlainObject(target[key]))
                        target[key] = {}
    
                    // source[key] 是数组,而 target[key] 不是数组,则 target[key] = [] 初始化一下,否则递归会出错的
                    if (isArray(source[key]) && !isArray(target[key]))
                        target[key] = []
                    // 执行递归
                    extend(target[key], source[key], deep)
                }
                // 不满足以上条件,说明 source[key] 是一般的值类型,直接赋值给 target 就是了
                else if (source[key] !== undefined) target[key] = source[key]
        }

    内部实现其实也是差不多。

  • 相关阅读:
    android gridview布局,实现长按某一个,所有项都显示删除的图标
    Error:Execution failed for task ':app:transformClassesWithDexForDebug&#
    FragmentActivity和Activity的具体区别在哪里
    android批量文件上传(android批量图片上传)
    Android studio修改debug.keystore
    2 weekend110的SecureCRTPortable远程连接 + 上传安装jdk + 上传安装配置hadoop
    1 weekend110的Linux带图形系统安装 + 网络配置 + 静态IP设置
    weekend110(Hadoop)的 第一天笔记
    详细讲解Hadoop源码阅读工程(以hadoop-2.6.0-src.tar.gz和hadoop-2.6.0-cdh5.4.5-src.tar.gz为代表)
    如何自己编译生成hadoop的eclipse插件,如hadoop-eclipse-plugin-2.6.0.jar
  • 原文地址:https://www.cnblogs.com/yelongsan/p/9051308.html
Copyright © 2011-2022 走看看