博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

浅谈Javascript 浅拷贝和深拷贝的理解

Posted on 2019-05-14 17:21  Hamilton Tan  阅读(241)  评论(0编辑  收藏  举报

   javascript中存储对象都是存地址的。

   浅拷贝:浅拷贝是都指向同一块内存区块,浅拷贝共用同一内存地址,你改值我也变。如果拷贝的对象里面的值是一个对象或者数组,它就是浅拷贝,拷贝的知识引用地址。 jquery的extend方法都是浅拷贝,一般的等号赋值也是浅拷贝 。

准确的理解:

Object.assign,针对是只是一些简单的对象(针对基础类型)
 
Object.assign({a: 1, b: 'asv', c:false})这个是深拷贝
 
Object.assign({a: [1,2], b:{e: 'asv', f: 'asd'}})这个不是深拷贝

    

     上面vue里面的两个写法也是浅拷贝,具体地址为 https://cn.vuejs.org/v2/guide/list.html

 

   深拷贝:深拷贝则是另外开辟了一块区域,深拷贝是互不影响,你改值我也不变。angular里面的 angular.copy 是深拷贝。

   下面实例也可以看出这一点:

// 浅拷贝
const a = {t: 1, p: 'gg'};
const b = a;
b.t = 3;
console.log(a); // {t: 3, p: 'gg'}
console.log(b); // {t: 3, p: 'gg'}
//深拷贝
const c = {t: 1, p: 'gg'};
const d = deepCopy(c);
d.t = 3;
console.log(c); // {t: 1, p: 'gg'}
console.log(d); // {t: 3, p: 'gg'}
//浅拷贝  es6 扩展运算符
let a = [14,12,54,33,22];
let b = a;  // 相当于copy
a.push(44);
console.log(a); // [14, 12, 54, 33, 22, 44]
console.log(b); // [14, 12, 54, 33, 22, 44]

//深拷贝
let a = [14,12,54,33,22];
let b = [...a];
a.push(44);
console.log(a); // [14, 12, 54, 33, 22, 44]
console.log(b); // [14, 12, 54, 33, 22]

 

可以明显看出,浅拷贝在改变其中一个值时,会导致其他也一起改变,而深拷贝不会。

  Object.assign() ————深拷贝神器,这个方法就是用来拷贝一个对象的,通常做法就是Object.assign({}, sourceObject, { key1: value1,key2: value2})  {}表示目标对象,会定义成一个空的{},sourceObjec就是源对象
// Cloning an object
var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
// Merging objects
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };

var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.
console.log(o2);//{b: 2} 源对象没有变
console.log(o3);// {c: 3}

是不是很完美,又可以clone又可以merge。在我这种情况下,我觉得我的代码量又可以减少了,比如:

const defaultOpt = {
    title: 'hello', 
    name: 'oo', 
    type: 'line'
};
// 原来可能需要这样
const opt1 = deepCopy(a);
opt1.title = 'opt1';
opt1.type = 'bar';
opt1.extra = 'extra'; // 额外增加配置
// 现在只要这样
const opt2 = Object.assign({}, a, {
    title: 'opt2', 
    type: 'bar', 
    extra: 'extra'
});
注:它只对顶层属性做了赋值,完全没有继续做递归之类的把所有下一层的属性做深拷贝。意思就是是拷贝一层,没有拷贝多层。
一层
{
   a: 1,
   b: 2,
}

多层
{
   a: 1,
   b: 2,
   c: {
      d: 4,
      e: {
          f: 6,
          g: 7
      }
   }
}

实现深拷贝,遍历key

function deepClone(obj){
    //判断obj是否为数组,如果是,初始化数组[],否则初始化对象{}
    let dcObj = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        //循环
        for(key in obj){
            //判断对象是否有key属性
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    dcObj[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    dcObj[key] = obj[key];
                }
            }
        }
    }
    return dcObj;
};
let t1 = {
    "a": 1,
    "b": 2,
    "c": {
        "d": 4,
        "e": {
            "f": 6,
            "g": 7
        }
    }
};
let t2=deepClone(t1);
t1.a=6;
console.log(t1);
/*
{
    "a": 6,
    "b": 2,
    "c": {
        "d": 4,
        "e": {
            "f": 6,
            "g": 7
        }
    }
}
*/
console.log(t2); //t1深拷贝给t2, b属性的值还是为2,没有改变,所以是深拷贝
/*
{
    "a": 1,
    "b": 2,
    "c": {
        "d": 4,
        "e": {
            "f": 6,
            "g": 7
        }
    }
}
*/

 

 
   
 
0
 
深拷贝是指是否完整的复制了一个数据。
var a = 1;
var b = a;
这也是深拷贝
 
Object.assign,针对是只是一些简单的对象(针对基础类型)
 
Object.assign({a: 1, b: 'asv', c:false})这个是深拷贝
 
Object.assign({a: [1,2], b:{e: 'asv', f: 'asd'}})这个不是深拷贝
 
{...item,...that.ctgdata  }; 这个只是Object.assign的一个新语法。
 
数组是引用类型。
 
vue set是针对引用类型不会更新的,set是针对引用不会更新的.
你都已深拷贝了,还set做什么。
 
js 深拷贝
 
export const deepCopy = (obj, cache = []) => {
if (obj === null || typeof obj !== 'object') {
return obj
}
const hit = cache.filter((c) => c.original === obj)[0]
if (hit) {
return hit.copy
}
const copy = Array.isArray(obj) ? [] : {}
 
cache.push({
original: obj,
copy,
})
 
Object.keys(obj).forEach((key) => {
copy[key] = deepCopy(obj[key], cache)
})
 
return copy
}
 
你有时间可以去看一下,引用类型和值类型的区别。主要看原理,不是定义。主要看一下原理,引用类型是怎么产生的。内存中引用类型与值类型是怎么实现的。
你了解这些后,应该就知道怎么处理这种问题了。引用类型也是有很多好处的。引用类型 比 值类型 有一个数据共享的优势。
 
 
 
主要看一下原理,引用类型是怎么产生的。
内存中引用类型与值类型是怎么实现的。
你了解这些后,应该就知道怎么处理这种问题了。
 
引用类型 是什么,怎么产生的,存在的意义,这些你能明白就不存在问题了。
引用类型是计算器发展史中一次重要突破。
没有引用类型的计算机,是会存在很多无法解决的问题。
没有引用类型,30G内存都不够用。:你要去看一下,内存的结构:堆和栈。什么是堆,什么是栈。它们分别是什么?这些东西,不是一下说的清楚的,你要去累积这些知识。
知识传递 不是 复制拷贝。
 
vue 视图更新和深拷贝有啥关系?
视图更新,本身没有和这个有关系。
但视图更新是需要它知道,数据改变了。为什么存在这个问题,就得了解引用类型是什么?
 
引用类型是js 特有的,只要是计算机语言都有。
 
 
0
 
存储方式得不同,就引起了数据更新的原理问题。
 
栈更新 和 堆更新。
栈更新是什么原理,堆更新又是什么原理?它们之前存在什么样的区别,又有什么样的联系?
数据在内存中的存储方式?
你现在去看一下存储方式
 

JavaScript的数据类型
1.基本数据类型
number string boolean null undefined

 

2.引用数据类型 (简单来说,就是能用new关键字创建的都是引用数据类型)
array(数组) function(函数) regexp(正则) object(对象)

 

栈与堆
栈(stack) :用来保存简单的数据字段 (先进后出)
堆(heap): 用来保存栈中简单数据字段对指针的引用 (队列优先,先进先出)

 

*基本数据类型都存在于栈中,引用数据类型存在于堆中(可以这样理解)
*对于原始类型的值而言,其地址和具体内容都存在与栈内存中;而基于引用类型的值,其地址存在栈内存,其具体内容存在堆内存中。

 

真正的拷贝,就是拷贝过后,arr1和arr2指向的不再是同一片内存地址,而是分别指向各自的地址,这样发生变化的时候就不会出现同时变化,这就是深拷贝。

https://www.jb51.net/article/145470.htm  

 

https://juejin.cn/post/6868166748709847053

 

https://github.com/zhuanyongxigua/blog/issues/3

 

 参考:https://www.jianshu.com/p/a66050673663 

 

// Deep Clone
  obj1 = { a: 0 , b: { c: 0}};
  let obj3 = JSON.parse(JSON.stringify(obj1));
  obj1.a = 4;
  obj1.b.c = 4;
  log(JSON.stringify(obj3));
  // { a: 0, b: { c: 0}}