JavaScript - 深浅拷贝
前言
JS 中的数据类型可分为两种:
- 基本类型:
undefined
,null
,Boolean
,String
,Number
,Symbol
- 引用类型:
Object
,Array
,Date
,Function
,RegExp
等
不同类型的存储方式:
- 基本类型:基本类型值在内存中占据固定大小,保存在
栈内存
中 - 引用类型:引用类型的值是对象,保存在
堆内存
中,而栈内存
中存储的是对象的变量标识符
以及对象在堆内存
中的存储地址
不同类型的复制方式:
- 基本类型:从一个变量向另外一个新变量复制基本类型的值,会创建这个值的一个副本,并将该副本复制给新变量
- 引用类型:从一个变量向另一个新变量复制引用类型的值,其实复制的是储存地址,最终两个变量最终都指向同一个对象
深拷贝和浅拷贝的区别
- 浅拷贝:仅仅是复制了引用,彼此之间的操作会互相影响
- 深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响
浅拷贝
1、可通过赋值实现
js
123456789101112131415161718192021222324252627282930function shallowClone(object) { // 只拷贝对象 if (!object || typeof object !== 'object') return
// 根据 object 的类型判断是新建一个数组还是对象 let newObject = Array.isArray(object) ? [] : {}
// 遍历 object,并且判断是 object 的属性才拷贝 for (let key in object) { if (object.hasOwnProperty(key)) { newObject[key] = object[key] } }
return newObject}
let obj = { a: 'hello', b: { c: 'js', },}
let newObj = shallowClone(obj)newObj.a = 'HELLO'newObj.b.c = 'JS'
console.log(obj.a) // => helloconsole.log(obj.b.c) // => JS
2、可通过 Object.assign
实现
js
12345678910111213let obj = { a: 'hello', b: { c: 'js', },}
let newObj = Object.assign({}, obj)newObj.a = 'HELLO'newObj.b.c = 'JS'
console.log(obj.a) // => helloconsole.log(obj.b.c) // => JS
3、可通过 扩展运算符(...)
实现
js
12345678910111213let obj = { a: 'hello', b: { c: 'js', },}
let newObj = { ...obj }newObj.a = 'HELLO'newObj.b.c = 'JS'
console.log(obj.a) // => helloconsole.log(obj.b.c) // => JS
深拷贝
1、可通过 JSON.parse(JSON.stringify(object))
实现
js
12345678910111213let obj = { a: 'hello', b: { c: 'js', },}
let newObj = JSON.parse(JSON.stringify(obj))newObj.a = 'HELLO'newObj.b.c = 'JS'
console.log(obj.a) // => helloconsole.log(obj.b.c) // => js
但是该方法也是有局限性的:
会忽略 undefined 会忽略 symbol 不能序列化函数 不能解决循环引用的对象2、可通过递归实现
js
12345678910111213141516171819202122232425262728function deepClone(obj) { if (obj === null) return obj if (obj instanceof Date) return new Date(obj) if (obj instanceof RegExp) return new RegExp(obj) if (typeof obj !== 'object') return obj
let cloneObj = new obj.constructor() for (let key in obj) { if (obj.hasOwnProperty(key)) { cloneObj[key] = deepClone(obj[key]) } } return cloneObj}
let obj = { a: 'hello', b: { c: 'js', },}
let newObj = deepClone(obj)newObj.a = 'HELLO'newObj.b.c = 'JS'
console.log(obj.a) // => helloconsole.log(obj.b.c) // => js