赋值
- 当我们把一个对象赋值给一个新的对象时,赋的是该对象在栈中的地址,而不是堆中的数据 也就是赋值之后,两个对象指向同一个存储空间。无论哪个对象发生改变,其实都是改变的堆中的数据
let a = {
name: 'a',
like: ['吃饭']
}
let b = a
b.name = 'b'
b.like[0] = '睡觉'
console.log('a', a)
console.log('b', b)
当我们把一个变量赋值给另一个变量时,是在栈内存中新增一个变量,并且赋值。二者互不影响
let c = 1
let d = c
d = 2
console.log(c) //c1
console.log(d) //2
浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
利用{...}实现拷贝
。注意这里是拷贝,先不区分是深还是浅
如果是引用类型,且只有一层对象,则这一层就是深拷贝。拷贝前后,对象的基本类型互不影响,
如果是引用类型,且有两层以上嵌套对象,则这嵌套的,就是浅拷贝。嵌套的对象,拷贝的嵌套对象的引用
。拷贝前后,嵌套对象数据相互影响,一层对象,互不影响,
let obj = {
name: 1,
address: {
x: 1
}
}
let d = { ...obj }
d.name = 4 // 一层 深拷贝,互不影响
d.address.x = 'x' // 两层,拷贝的事地址,相互影响
console.log('浅拷贝', obj)// 浅拷贝 { name: 1, address: { x: 'x' } }
console.log('浅拷贝', d)//浅拷贝 { name: 4, address: { x: 'x' } }
- 修改一层对象的d.name。你会发现互不影响。
因为深拷贝
- 修改嵌套对象的d.address.x.你会发现相互影响,
因为是浅拷贝
浅拷贝实现
function shallowCopy (obj) {
let target = {}
for (let k in obj) {
if (obj.hasOwnProperty(i)) { // 不遍历其原型链上的属性
target[i] = obj[i]
}
}
return target
}
for-in 循环有一个问题,不仅会遍历对象的实例属性,还会遍历从原型链继承来的属性,用hasOwnProperty 过滤出对象的实例属性
深拷贝
在堆内存,开辟一块新的区域存放对象,对对象中的子对象进行递归拷贝,拷贝前后,两个对象互不影响
function deepClone(obj, hash = new Map()) {
if (obj == null) return obj // 如果是null和undefined 就不进行操作
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return RegExp(obj)
// 如果是普通值,不需要拷贝
if (typeof obj !== 'object') return obj
// 是对象的话,就要进行深拷贝
if (hash.get(obj)) return hash.get(obj)
let cloneObj = new obj.constructor
hash.set(obj, cloneObj)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj
}
let obj = {
name: 1,
address: {
x: 1
}
}
let d = deepClone(obj)
d.address.x = 4
console.log(obj, d)//{ name: 1, address: { x: 1 } } { name: 1, address: { x: 4 } }
- 如果出现循环引用,则需要使用WeakMap
快捷方式进行浅拷贝
- Object.assign({},) // 一层是深拷贝,两层以上是浅拷贝
- {...} // 一层是深拷贝,两层以上是浅拷贝
- loadsh
深拷贝
- JSON.parse(JSON.stringify())
- lodash
JSON.parse(JSON.stringify())
- 将js对象序列化为json字符串,然后再反序列化为js原对象
- 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
- 如果obj里有RegExp、Error对象 ,序列化之后会得到空对象
- 如果obj里有时间对象 ,序列化之后会得到字符串 5.只能序列化对象的可枚举的自由属性,,如果obj中的对象由构造函数生成,拷贝后,会丢失对象constroter
总结
- 赋值的实现,只是一个
=
,而 浅拷贝是创建一个新的{}
,那么就可以得知,赋值时与原数据指向同一对象,而浅拷贝则指向了不同对象。 - 拷贝是拷贝对象的(引用数据类型),所以你要是基本数据类型,用赋值就行了
- 手写深拷贝有几个点,很不错,一个是对,null和undefined的判断,因为null == nudefined,所以这两个判断一个就可以了
- 判断是数组还是对象,巧妙用到了new obj.constructor。他会返回你要拷贝的数据,并且可以得到传入的类型。这就比这行代码省事 Array.isArray(obj) ? [] : {}
- 如果出现循环引用,用 new WeakMap 。