浅拷贝和深拷贝的含义及实现
含义
当你复制一个值给另一个值的时候,假如复制后的值跟之前的值不会再有任何关系,就叫深拷贝,否则就叫浅拷贝。
那么什么时候才会有关系,什么时候没有呢?例子:
let str = 'string'
let strA = str
strA = 'changed'
console.log(str, strA) // string, changed 深拷贝
let obj = {name: 'sifan'}
let objA = obj
objA.age = 22
console.log(obj, objA) // {name: 'sifan', age: 22}, {name: 'sifan', age: 22} 浅拷贝
str为什么不变呢?obj又为什么变呢?这个时候需要引入基本类型的坑了。
JavaScript基本类型有number,string,boolean,null,undefined,symbel(ES6新增),Bigint(ES10新增),还有引用类型object => Array,Function,Date, Regexp,对于不同的类型存储方式也不同。
基本类型使用栈内存,它们的数据在一个栈当中,这种也叫做值类型:
引用类型使用堆内存,它们的名在栈当中,但是值在堆里面,名字指向的是位置:
现在懂了吧,基本类型两个存的值都是分开的,所以很明显八竿子打不着;引用类型的堆地址相同,指向的就是同一个堆,所以改变值会让里面的东西都发生改变。
那么怎样才能让它不指向同一个堆地址呢,很简单,让objA指向新的地址就可以了,也就是在堆里面再开辟一个空间,这样他们就不会有关联了。
实现方法
JSON
缺点:会忽略undefined,symbol,不能序列化函数,不能解决循环引用的对象
let obj = {
name: 'sifan',
age: 21,
hobby: ['play game','book'],
cb: function() {
console.log('wa ou')
},
reg: /"/g,
data: new Date()
}
let objA = JSON.parse(JSON.stringify(obj))
objA.sex = 'male'
console.log(obj, objA);
递归实现
let obj = {
name: 'sifan',
age: 21,
hobby: ['play game','book'],
cb: function() {
console.log('wa ou')
},
reg: /"/g,
data: new Date()
}
function deepClone(paramter) {
let result = Array.isArray(paramter) ? [] : {}
if(paramter && typeof paramter === "object") {
if(Object.prototype.toString.call(paramter) === '[object RegExp]') {
result = new RegExp(paramter)
} else if(Object.prototype.toString.call(paramter) === '[object Date]') {
result = new Date(paramter)
} else {
for(let i in paramter) {
if(paramter[i] && typeof paramter[i] === 'object') {
result[i] = deepClone(paramter[i])
} else {
result[i] = paramter[i]
}
}
}
}
return result
}
let objA = deepClone(obj)
console.log(obj, objA);
嘿嘿嘿,看,简简单单你就把他写出来了。所以做什么细心就欧克了。冲冲冲!!!
lodash
npm install lodash // 安装依赖
import _ from 'lodash';
const objA = _.cloneDeep(obj);
还有哦
对于简单数组的深拷贝可以非常简单:
slice
let arr = [1,2,3,4,5,6]
let brr = arr.slice()
arr.push(100)
brr.shift()
console.log(arr, brr)
...运算符
let arr = [1,2,3,4,5,6]
let brr = [...arr]
arr.push(100)
brr.shift()
console.log(arr, brr)
分割线
浅拷贝实现
对象
Object.assign()
...(es6)
注意,需要用{}去接。 // {...obj}
数组
Array.prototype.slice(start, end)
基于当前数组,从开始下标(包含),到结束下标(不包含),返回一个新数组,不影响原数组
Array.prototype.concat(多个参数)
合并参数为一个新的数组
Array.from(参数)
将一个类数组转化为数组
...(es6扩展运算符)
行百里者半九十