文章目录
- JavaScript中的变量类型
- 深拷贝和浅拷贝的理解
- 深拷贝和浅拷贝的实现方式
- 为什么需要深拷贝和浅拷贝
JavaScript中的变量类型
(1)、基本类型
JavaScript中的基本类型有五种: null、undefined、boolean、string、number。 变量是按值存放的,存放在栈中的简单数据段,可以直接访问。
(2)、引用类型
引用类型包括对象和数组,其存储在堆当中,而变量是指针,指向堆。 当我们访问的时候,实际上是访问指针,然后指针去寻找对象或数组。
深拷贝与浅拷贝的理解
(1)、深拷贝
先新建一个空对象,内存中新开辟一块地址,把被复制对象的所有可枚举的(注意可枚举的对象)属性方法一一复制过来,注意要用递归来复制子对象里面的所有属性和方法,直到子子.....属性为基本数据类型。关键点:开辟新内存、递归复制。
另外一种定义:深拷贝指的是对象属性所引用的对象全部进行新建对象复制,以保证深复制的对象的引用图不包含任何原有对象或对象图上的任何对象,隔离出两个完全不同的对象图。
(2)、浅拷贝
一个对象复制另外一个对象,如果不是深拷贝,就是浅拷贝。 简单地说,浅拷贝就是将一个对象的内存地址的“”编号“”复制给另一个对象。即在真正访问的时候还是会访问到被复制对象。 或者只是深拷贝了第一层的引用类型,而没有拷贝更深层次的应用类型,而是利用复制地址的方式,这也是浅拷贝。
深拷贝和浅拷贝的实现方式
浅拷贝
浅拷贝1
var obj1 = { name: ‘wayne‘ } var obj2 = obj1 obj2.name = ‘hedy‘ console.log(obj1.name) // ‘hedy‘
这里首先创建了一个 obj1 对象,然后将obj复制给了 obj2, 但是这里仅仅是指针的复制,所以在修改 obj2.name 的时候,实际上是修改的同一个堆中的对象,既浅拷贝。
浅拷贝2
var obj = { a: 1, b: { d: { e: ‘test‘ } }, c: [1, 2, 3] } function shallowClone1(copyObj) { var newObj = {}; for (var prop in copyObj) { newObj[prop] = copyObj[prop]; } return newObj; } var newObj = shallowClone1(obj); console.log(newObj.b.d === obj.b.d); // true
即通过for in的形式将对象进行复制,这里可以看到复制只是对于指针的复制,得到的新的对象还是指向同一个堆中的对象,所以是浅复制。
浅拷贝3
Object.assign()
var obj1 = { name: ‘wayne‘, age: 22, other: { hobby: ‘table‘, school: ‘xjtu‘ } } var obj2 = Object.assign({}, obj1); obj2.name = ‘hedy‘ console.log(obj1.name) // wayne obj2.other.hobby = ‘sing‘ console.log(obj1.other.hobby) // sing
只从表面上来看,似乎Object.assign()的目标对象是{},是一个新的对象(开辟了一块新的内存空间),是深拷贝。
当我们修改obj2.name的时候,obj1.name没有改变,但是当我们修改 obj2.other.hobby 的时候,obj1.other.hobby 同样也发生了变化。
即Object.assign()也是浅拷贝,或者说只是深拷贝了第一层,这样我们认为它还是浅拷贝。
浅拷贝4
concat
var a = [2, [3,5,7], {name: ‘wayne‘}]; var b = a.concat(4) a[0] = 3; console.log(b[0]) // 2 看起来像深复制 a[1][0] = 666; console.log(b[1][0]) // 666 浅复制 a[2].name = ‘hedy‘ console.log(b[2].name) // hedy 浅复制
可以看到通过concat返回的新数组,只有改变其中一个的布尔值、字符串、数值,另一个不会改变,但是改变其中的对象、数组时,可以发现,另一个也在同时改变,即还是引用原来的堆中的内容。
slice
var a = [2, [3,5,7], {name: ‘wayne‘}]; var b = a.slice(0) a[0] = 3; console.log(b[0]) // 2 看起来像深复制 a[1][0] = 666; console.log(b[1][0]) // 666 浅复制 a[2].name = ‘hedy‘ console.log(b[2].name) // hedy 浅复制
这段代码仅仅是将上一段中的concat修改为了slice,发现结果也是一样的,即slice方法得到的也是浅复制。
深拷贝
深拷贝1
JSON.stringify() 和 JSON.parse()
var obj1 = { name: ‘wayne‘, age: 22, other: { hobby: ‘table‘, school: ‘xjtu‘ } } var obj2 = JSON.parse(JSON.stringify(obj1)); obj2.name = ‘hedy‘ console.log(obj1.name) // wayne obj2.other.hobby = ‘sing‘ console.log(obj1.other.hobby) // table
可以看出通过JSON.stringify先将对象转化为字符换,然后再通过JSON.parse()转化为对象,这个对象就是完全在开辟的新的内存空间中的对象 。
深拷贝2
jquery $.clone(true) / $.extend(true)
var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ] }; var y = $.extend({}, x), //浅复制 z = $.extend(true, {}, x); //深复制 console.log(y.b.f === x.b.f ) // true console.log(z.b.f === x.b.f) // false
可以看到通过 $.extend() 传入第一个参数 true, 就可以进行深复制了。
推荐文章:https://www.zhihu.com/question/23031215
http://blog.csdn.net/waiterwaiter/article/details/50267787