深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。
深拷贝:
深拷贝复制变量值,对于非基本类型变量,则递归至基本类型变量后,再复制。深拷贝后的对象与原来的对象是完全隔离的,并不互相影响,对一个对象的修改并不会影响另一个对象。
浅拷贝:
浅拷贝是会将对象的每个属性进行依次复制。但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时,也会随着改变。
可以使用for in , object.assign, 扩展运算符...,Array.prototype.slice(),Array.prototype.concat()等,例如:
let obj = {
name: 'sam',
age: 19,
hobbies: ['reading', 'photography']
}
let obj2 = Objcet.assign({}, obj);
let obj3 = {...obj };
obj.name = 'jack';
obj.hobbies.push('coding');
console.log(obj); //{name: "jack", age: 19, hobbies: ["reading", "photography", "coding"]}
console.log(obj2); //{name: "sam", age: 19, hobbies: ["reading", "photography", "coding"]}
console.log(obj3); //{name: "sam", age: 19, hobbies: ["reading", "photography", "coding"]}
可以看出浅拷贝只对第一层的属性进行拷贝,当第一层的属性值是基本数据类型时,新的对象和原来的对象互不影响。但是如果第一层的属性是复杂数据类型,那么新对象和原有对象的属性值其指向的是同一块内存地址。
深拷贝实现:
深拷贝最简单的实现是:JSON.parse(JSON.stringify(obj));
但有一些缺陷:
1对象的属性值是函数无法拷贝,
2原型链上的属性无法拷贝
3不能正确处理date类型的数据
4不能处理RegExp
5会忽略symbol
6会忽略undefined
deepClone函数
1如果是基本数据类型,直接返回,
2如果是RegExp或者Date类型,返回对应类型
3如果是复杂数据类型,递归
4考虑循环引用问题
function deepClone(obj, hash = new WeakMap()) { //递归拷贝
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);
if (obj === null || typeof obj !== 'object') {
//如果不是复杂数据类型,直接返回
return obj;
}
if (hash.has(obj)) {
return hash.get(obj);
}
//如果obj是数组,那么obj.constructor 是[Function:Array]
//如果obj是对象,那么obj.constructor 是[Function:Object]
let t = new obj.constructor();
hash.set(obj, t);
for (let key in obj) {
//递归
if (obj.hasOwnProperty(key)) { //是否是自身的属性
t[key] = deepClone(obj[key], hash);
}
}
return t
}
export function deepClone(source) {
if (!source && typeof source !== "object") {
throw new Error("error arguments", "shallowClone");
}
const targetObj = source.constructor === Array ? [] : {};
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === "object") {
targetObj[keys] = deepClone(source[keys]);
} else {
targetObj[keys] = source[keys];
}
});
return targetObj;
}
export function uniqueArr(arr) {
return Array.from(new Set(arr));
}
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path);
}