变量存储
基本数据类型: string、number、boolean、null、undefined、symbol
基本类型是值存储在栈中的,值是不可变的,无论是复制还是拼接等都会创建一个新的值
引用数据类型:Object、Array、Map、Set、Date...
引用类型是值存储在堆中的,其引用(即物理地址)是存储在栈中,我们通常也是通过其引用访问和修改存储在堆中的值。所以,对于对象类型来说,赋值表达式仅仅使新的变量和被复制的对象指向同一块内存空间而已,所以深度拷贝的话,需要对每个对象都创建一块新的内存空间存值
浅拷贝
即仅拷贝对象的第一层属性,若第一层属性的值是个普通对象,那么拷贝后对象和原对象他们的属性值是相同的
深拷贝
即完全复制对象,拷贝后的对象与原对象不再存在任何联系
浅拷贝 & 深拷贝代码实现
function getType(val) {
return Object.prototype.toString.call(val).toLowerCase().slice(8, -1);
}
function hasOwnProperty(ctx, prop) {
return Object.prototype.hasOwnProperty.call(ctx, prop);
}
function cloneArray(arr) {
return arr.map(item => deepClone(item));
}
function clonePlainObject(obj, map, options = {}) {
if (map.has(obj)) return map.get(obj);
const newObj = {};
if (!(options.isBreakPropsReferenced && map.size)) map.set(obj, newObj);
for (key in obj) {
if (hasOwnProperty(obj, key)) newObj[key] = deepClone(obj[key], map, options);
}
cloneSymbolProps(obj, newObj);
return newObj;
}
function cloneSymbolProps(obj, newObj) {
if (getType(Object.getOwnPropertySymbols) === 'function') {
const symKeys = Object.getOwnPropertySymbols(obj);
symKeys.forEach(key => newObj[key] = deepClone(obj[key]));
}
}
function deepClone(source, map = new Map(), options = {}) {
const type = getType(source);
let result = source;
switch (type) {
case 'object':
result = clonePlainObject(source, map, { ...options });
break;
case 'array':
result = cloneArray(source);
break;
case 'map':
result = new Map(source.entries());
break;
case 'set':
result = new Set(source.values());
break;
case 'date':
result = new Date(source);
break;
case 'regexp':
result = new RegExp(source);
break;
default:
result = source;
break;
}
return result;
}
function shadowClone(source) {
const type = getType(source);
let result;
switch (type) {
case 'object':
result = {};
if (typeof Reflect === 'object' && getType(Reflect.ownKeys) === 'function') {
result = Reflect.ownKeys(source).reduce((prev, cur) => {
prev[cur] = source[cur];
return prev;
}, {});
} else {
for (key in source) {
if (hasOwnProperty(source, key)) {
result[key] = source[key];
}
}
if (getType(Object.getOwnPropertySymbols) === 'function') {
const symKeys = Object.getOwnPropertySymbols(source);
symKeys.forEach(key => result[key] = source[key]);
}
}
break;
case 'array':
result = [...source];
break;
case 'map':
result = new Map(source.entries());
break;
case 'set':
result = new Set(source.values());
break;
case 'date':
result = new Date(source);
break;
case 'regexp':
result = new RegExp(source);
break;
default:
result = source;
break;
}
return result;
}
function clone(source, options = {}) {
let map = new Map();
let result;
if (options.isDeep) {
result = deepClone(source, map, options);
} else {
result = shadowClone(source);
}
map.clear();
map = null;
return result;
}
const log = console.log;