深拷贝/浅拷贝

深浅拷贝

数据类型及特点

  • 基本数据类型
    Undefined, Null, Symbol, Boolean, String, Number
    特点:直接存储在栈中的数据
  • 对象数据类型(引用数据类型)
    特点:栈中存储的是对该对象的引用,真实的数据存储在堆内存中

引用数据类型在栈中存储了指针,该指针指向堆中的该实体的起始地址,当解释器寻找引用值时,
会首先检索其在栈中的地址,取得地址后从堆中获得实体

深浅拷贝

深浅拷贝只针对Object或者Array这样的引用数据类型

浅拷贝只复制指向对象的指针地址,而不是复制对象本身,新旧对象还是共享同一块内存。
深拷贝会创造另外一个一摸一样的对象,新旧对象不共用同一块内存,修改新对象不会影响到旧对象。

浅拷贝与赋值的区别

var obj1 = {name: 'LiLy', age: '12', language: ['english', 'chinese', 'frech']}
  • 赋值: 赋的是该对象在栈中的地址,而不是堆中的数据。
    两个对象指向的是同个存储空间,无论哪个对象发生改变,其实改变的都是原对象,是联动的。

          var obj2 = obj1
          obj2.name = 'Lucy'
          obj2.language[0] = 'India'
          
          obj1 和 obj2 都是
          
          {
              age: "12"
              language: (3) ["India", "chinese", "frech"]
              name: "Lucy"
           }
    
  • 浅拷贝:按位拷贝对象,它会创建一个对象,这个对象有着原始对象属性值的一份精确拷贝。
    如果属性是基本数据类型,拷贝的就是基本类型的值;
    如果属性是引用数据类型,拷贝的就是内存地址。
    因此如果其中一个对象改变了这个内存地址,就会影响到另一个对象

function shallowCopy(src) {
    var dst = {}
    for (var prop in src) {
        if (src.hasOwnProperty(prop)) {
            dst[prop] = src[prop]
        }
    }
    return dst
}

var obj3 = shallowCopy(obj1)

obj3.name = 'Bob'
obj3.language[0] = 'lan3'

obj3: {
      age: "12"
      language: (3) ["lan3", "chinese", "frech"]
      name: "Bob"
}

obj1: {
    age: "12"
    language: (3) ["lan3", "chinese", "frech"]
    name: "Lucy"
}

总结:

-- 和原数据是否指向同个对象 第一层数据为基本数据类型 元数据中包含子对象
赋值 改变会使原数据一同改变 改变会使原数据一同改变
浅拷贝 改变不会使原数据一同改变 改变会使原数据一同改变
深拷贝 改变不会使原数据一同改变 改变不会使原数据一同改变

浅拷贝实现方式

  1. Object.assign()

把任意多个源对象的可枚举属性,拷贝给目标对象,然后返回目标对象。其中拷贝的是对象属性的引用,而不是对象属性身。

var obj1= {
    age: "12",
    language: (3) [1, 2, 3],
    name: "a",
    love: {son: 'tom'}
};
        
var obj2=Object.assign({}, obj1);  // obj2: {age: "12", language: undefined, name: "a",love: {son: "tom"}}

obj2.love.son='Bob';              // obj2: {age: "12", language: undefined, name: "a",love: {son: "Bob"}}, obj1: {age: "12", language: undefined, name: "a",love: {son: "Bob"}}

// 当object 只有一层的时候,是深拷贝
obj2.age='13'                   // obj2: {age: "13", language: undefined, name: "a"}, obj1: {age: "12", language: undefined, name: "a"}
  1. Array.prototype.concat()

修改新对象的子对象会改到原对象

var arr1=[1,2,{name: 'a1'}];
var arr2=arr1.concat();  // arr2: [1,2,{name: 'a1'}]
arr2[1] = 3;             // arr1:[1,2,{name: 'a1'}], arr2: [1,3,{name: 'a1'}]
arr2[2].name='a2';       // arr1:[1,2,{name: 'a2'}], arr2: [1,3,{name: 'a2'}]
  1. Array.prototype.slice()

修改新对象的子对象会改到原对象

var arr1=[1,2,{name: 'a1'}];
var arr2=arr1.slice();  // arr2: [1,2,{name: 'a1'}]
arr2[1] = 3;             // arr1:[1,2,{name: 'a1'}], arr2: [1,3,{name: 'a1'}]
arr2[2].name='a2';       // arr1:[1,2,{name: 'a2'}], arr2: [1,3,{name: 'a2'}]     

深拷贝实现方式

  1. JSON.parse(JSON.stringfy())
    先转换成JSON字符串再转成JSON对象,会产生新的对象,而且对象会开辟新的栈,实现深拷贝。

注意: 不能处理函数,因为JSON.stringfy()不能结束函数,转化结果为null

var arr = [1, 3, {
    username: ' kobe'
}];

var arr4 = JSON.parse(JSON.stringify(arr));

arr4[2].username = 'duncan';   // arr4:[1, 3, {username: 'duncan'}], arr: [1, 3, {username: 'kobe'}]

arr4[1]=2;                    // arr4:[1, 2, {username: 'duncan'}], arr: [1, 3, {username: 'kobe'}]
  1. 函数库lodash

_.cloneDeep函数实现深拷贝

var obj1={a: 's', {name: 'Lily'}};

var obj2 = _.cloneDeep(obj1); 
  1. 手写递归方法

遍历对象,数组,直到里面都是基本数据类型,再进行复制

function checkedType (target) {
    return Object.prototype.toString.call(target).slice(8, -1)
}

function clone (target) {
    // result 为最后返回的结果, tartgetType为目标数据类型
    let result, tartgetType = checkedType(target)
    
    if (tartgetType === 'Array') {
        result = []
    } else if (tartgetType === 'Object') {
       result = {}
   } else {
       // 基本数据类型,直接返回结果
        return target
   }
   
    for (var key in target) {
        // 遍历对象或数组,直到找到基本数据类型进行复制
        let value = target[key]
        if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
             //继续遍历获取到value值
              result[i] = clone(value)
        } else {
            result[i] = value
        }
    }
    return result
}    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345