浅拷贝: 创建一个新的对象,赋值给新的函数; 如果这个对象是基本数据类型,就是复制一份给了新的函数, 如果是引用类型,就是复制一份内存中的地址,改变一个值肯定会影响另一个对象
深拷贝: 把原来的数据源复制了一份,放到新的内存开辟空间里,两个数据源互不影响
先看看:浅拷贝
方法一: object.assign
objetct.assign 是 ES6 中object 的一个方法,可以用来浅拷贝
// 方法一 object.assign
// Object.assign(target, ...source) target:目标, source:目标源
let target = {}
let source = { a : { b : 1}}
let source1 = { c : { d : 1}}
Object.assign(target, source, source1)
console.log(target) // { a: { b: 1}, c: { d: 2} }
target.c.d= 2
console.log(target); // { a: { b: 1}, c: { d: 2} }
注意几点:
1.不能拷贝对象的继承属性
2.不能拷贝对象的不可枚举的属性
3.可以拷贝Symbol 类型的属性
可以这样理解:Object.assign 循环遍历原对象的属性,通过复制的方法 将赋值给目标对象的相应属性
let obj1 = {a:{b:1},sym:Symbol(1)}
Object.defineProperty(obj1,"innumerable",{
value:"不可枚举属性",
enumerable:false
})
let obj2 = {}
Object.assign(obj2,obj1)
obj1.a.b = 2
console.log("obj1",obj1);
console.log("obj2",obj2);
打印结果
方法二: 扩展运算符方式:
和object.assign 有着同样的缺陷
// 对象拷贝
let obj = { a:1, b:{ c: 1}}
let obj2 = {...obj}
obj.a = 2
console.log(obj) // { a:2, b:{c:1}}
obj.b.c = 2
console.log(obj)// { a:2, b:{c:2}}
// 数组拷贝
let arr = [1,2,3,4]
let arr2 = [...arr]
console.log(arr1); // [1,2,3,4]
console.log(arr2); // [1,2,3,4]
方法三: concat 拷贝数组
数组的concat 方法也是一个浅拷贝, concat 使用比较有局限性,只能使用数组中
// concat 拷贝数组
let arr = [1,2,3,4]
let arr1 = arr.concat()
arr1[0] = 10
console.log(arr) // [1, 2, 3, 4]
console.log(arr1) // [10, 2, 3, 4]
方法四: slice 拷贝数组
slice 方法也比较有局限性,只能使用数组类型
// slice 方法
let arr = [1,2,3,4]
let arr1 = arr.slice()
arr1[0] = 20
console.log(arr) // [1, 2, 3, 4]
console.log(arr1) // [20, 2, 3, 4]
上面的代码中可以看出,浅拷贝只能拷贝一层的对象,如果存在对象的嵌套,那么浅拷贝将无能为力;
因此深拷贝是为解决浅拷贝的问题
上面写了这么多,我的想法,能不能自己写一个浅拷贝
注意两点:
1.基本数据类型,基本的拷贝
2.引用类型,拷贝一层对象属性
const shallowCopy = (obj) => {
if(typeof obj === "object" && obj != null){
console.dir(obj);
let copy = Array.isArray(obj) ? [] : {}
for(let prop in obj){
console.log(prop,"prop");
if(obj.hasOwnProperty(prop)){
copy[prop] = obj[prop]
}
}
console.log(copy,"copy");
return copy
}else{
return obj
}
}
let b = ["a","b","c"]
let a ={
age:12
}
let c= shallowCopy(b)
console.log(c);
深拷贝:
1.先看看我们常用的 JSON.stringify(),把一个对象序列化成为 JSON 的字符串,并将对象里面的内容转换成字符串,最后再用 JSON.parse() 的方法将JSON 字符串生成一个新的对象
let obj1 = { a:1, b:[1,2,3]}
let str = JSON.stringify(obj1)
let obj2 = JSON.parse(str)
obj1.a = 2
obj2.b[0]= 10
console.log(obj1) // {a:2 , b:[1,2,3]}
console.log(obj2) // {a:1 , b:[10,2,3]}
通过 JSON.stringify 实现一个对象的深拷贝,通过改变 obj1 的 a属性,改变 obj2 的 b中数组,看结果,互相不影响, 这也是我们常用的
2. 通过递归的方式,写一个简单的递归
let obj1 = { a:1, b:[1,2,3],c:{d:1} }
function deepCopy(obj){
let copyObj = {}
for(let key in obj){
if(typeof obj[key] == "object"){
copyObj[key] = deepCopy(obj[key])
}else{
copyObj[key]= obj[key]
}
}
return copyObj
}
let obj2 = deepCopy(obj1)
obj2.a = 10
obj2.b[0] = 100
obj2.c.d = 2
console.log(obj2);
3. 还可以通过 $.extend() , 我对它的理解是 $.extend({},src1,src2), 将src1和src2 合并一个对象,如果有相同的,后面会覆盖前面的,
4.改善递归
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
const deepClone = function (obj, hash = new WeakMap()) {
if (obj.constructor === Date)
return new Date(obj) // 日期对象直接返回一个新的日期对象
if (obj.constructor === RegExp)
return new RegExp(obj) //正则对象直接返回一个新的正则对象
//如果循环引用了就用 weakMap 来解决
if (hash.has(obj)) return hash.get(obj)
let allDesc = Object.getOwnPropertyDescriptors(obj)
//遍历传入参数所有键的特性
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
//继承原型链
hash.set(obj, cloneObj)
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
}
return cloneObj
}
// 下面是验证代码
let obj = {
num: 0,
str: '',
boolean: true,
unf: undefined,
nul: null,
obj: { name: '我是一个对象', id: 1 },
arr: [0, 1, 2],
func: function () { console.log('我是一个函数') },
date: new Date(0),
reg: new RegExp('/我是一个正则/ig'),
[Symbol('1')]: 1,
};
Object.defineProperty(obj, 'innumerable', {
enumerable: false, value: '不可枚举属性' }
);
obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
obj.loop = obj // 设置loop成循环引用的属性
let cloneObj = deepClone(obj)
cloneObj.arr.push(4)
console.log('obj', obj)
console.log('cloneObj', cloneObj)