浅复制(浅拷贝)和深复制(深拷贝)

js存储机制

JS中对象分为基本类型和复合(引用)类型,基本类型存放在栈内存,复合(引用)类型存放在堆内存。
堆内存用于存放由new创建的对象,栈内存存放一些基本类型的变量和对象的引用变量。

1.基本类型
如 变量,在创建时会直接复制一份。

var a = 'hello men'
var b = a
b = "good"
console.log(a) // hello men
console.log(b) // good

当b改变时,不会影响初始变量a

2.复合类型
如数组、对象等,js就会做引用,原对象和拷贝对象会做关联

var arr1 = [1, 2, 3, 4]
var arr2 = arr1
arr2[4] = 9
console.log(arr1)  // [1,2,3,4,9]
console.log(arr2)  // [1,2,3,4,9]

arr2改变时arr1也随之改变,这里举了数组的例子,对象也类似。

浅与深的区别

为了让拷贝对象 和 原对象完全脱离关系,我们需要用到浅拷贝、深拷贝这两种方法。

浅拷贝只能复制根属性,做不到真正的全拷贝。
深拷贝能实现真正的全拷贝,与原对象分清界限。

浅拷贝

我们先从简单的入手,浅浅的拷贝一个数组。

  • 方法1
var array1 = ['a', 'b', 'c']
var array2 = array1.slice() // 单个复制数组元素
array2[3] = 'h'
console.log(array1) // ['a','b','c']
console.log(array2) // ['a','b','c', 'h']
  • 方法2
var arr1 = [1, 2, 3]
var arr2 = []
var copy = (arr1, arr2) => {
    arr1.forEach((element, index) => {
        arr2[index] = element
    })
    return arr2
}
arr2 = copy(arr1, arr2)
arr2[3] = 6
console.log(arr2) // [ 1, 2, 3, 6 ]
console.log(arr1) // [ 1, 2, 3 ]

array2只改变自己,不影响他人,是个好同志。
现在难度升级,我们在数组里加入了对象。

var array1 = ['a', 'b', 'c', {
    "name": 'leo'
}]
var array2 = array1.slice()
array2[3].name = 'mark'
console.log(array1) // ['a','b','c',{name: 'mark'}]
console.log(array2) // ['a','b','c',{name: 'mark'}]

这时候改变array2,array1也跟着变了,哦NO!

深拷贝

现在是该大英雄出场了,“深拷贝!深拷贝!”
不啰嗦,直接上代码。

// 创建了 一个带数组和对象的元素 (这还是比较简单的对象结构)
var array1 = ['a', 'b', 'c', { "name": 'leo'}, [8, 9]]
var array2 = []
// 创建拷贝方法 
var copy = (obj1, obj2) => {
    obj1.forEach((item, index) => {
               // 判断该索引值是否为数组
        if (obj1[index].constructor === Array) {
            obj2[index] = []
            obj1[index].forEach((subItem, subIndex) => {
                obj2[index][subIndex] = subItem
            })
                // 判断该索引值是否为对象
        } else if (obj1[index] && typeof obj1[index] === 'object') {
            obj2[index] = {}
            for (let element in obj1[index]) {
                obj2[index][element] = obj1[index][element]
            }
                // 不是对象,说明是属性,直接赋值
        } else {
            obj2[index] = item
        }
    })
    return array2
}

var array2 = copy(array1, array2)
// 改变对象的属性值
array2[3].name = 'mark'
// 改变数组的值
array2[4][1] = '5'
console.log(array2) // [ 'a', 'b', 'c', { name: 'mark' }, [ 8, '5' ] ] 
console.log(array1) // [ 'a', 'b', 'c', { name: 'leo' }, [ 8, 9 ] ] 

恭喜你,我们向要的都实现了!

深不可测的深拷贝 (递归)

有些对象是后台传给我们的,拿到之前不知道里面是什么结构。这种数据着么去拷贝呢?
是时候展示真正的技术了, 那就是递归
递,层层递进。归,归去来兮。
总结下来就是...还是不解释了。
看代码:

// 创建一个深层嵌套的对象
var json1 = {
    "name": "leo",
    "age": 20,
    "child": [{
            "eye": "blue",
            "child": [{
                "photo": "nice"
            }, {
                "photo": "beautiful"
            }]
        },
        {
            "eye": "red",
            "child": []
        }
    ]
}

var json2 = {}
function copy(obj1, obj2) {
    for (var name in obj1) {
        if (typeof obj1[name] === "object") {
            obj2[name] = (obj1[name].constructor === Array) ? [] : {}
           // 递归时改变当前参数位置,
           // 举例:当前name为child时,copy中的参数被替换为 (obj1.child, obj2.child)
            copy(obj1[name], obj2[name])
        } else {
            obj2[name] = obj1[name]
        }
    }
    return obj2
}

json2 = copy(json1, json2)
json2.child[0].eye = 'green'
console.log(json1) // child: [ { eye: 'blue', child: [Object] }
console.log(json2) // child: [ { eye: 'green', child: [Object] }

最简单的深拷贝

先把对象使用JSON.stringify()转为字符串,再赋值给另外一个变量,然后使用JSON.parse()转回来即可。

let a = {
  a1: 1,
  a2: '2',
  a3: [1, 2, 3, 4, 5, 6],
  a4: {
    deep1: 1,
    deep2: 2
  }
}

let b = JSON.parse(JSON.stringify(a))
console.log(b)
a.a4.deep1 = 99
console.log(a)  // a的属性值变动了
console.log(b) // b没变

new

顺带提下,用new 创建的对象,都是引用原对象的。new常常和构造函数一起出现,用于创建对象的继承关系。
我们应尽量避免引用类型的直接拷贝,这样会改变原对象的属性,在协同工作时会产生不可预期的错误。
可以用新的方法Object.create()来创建,或者定义一个空的 F(){} 构造函数做衔接。

参考: https://www.jianshu.com/p/0d7bd31ccf43

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

推荐阅读更多精彩内容