这是我参与 8 月更文挑战的第 7 天,活动详情查看: 8月更文挑战
数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。
基本数据类型的特点:直接存储在栈(stack)中的数据 引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。 JS中的变量类型分为值类型(基本类型)和引用类型;对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址的拷贝,最终两个变量指向同一份数据
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。只进行一层拷贝。- 但
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。无限层级拷贝。
| 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 | |
|---|---|---|---|
| 赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
| 浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
| 深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
浅拷贝实现方式
Object.assign()Array.prototype.concat()Array.prototype.slice()展开运算符...函数库lodash的_.clone方法
拷贝属性值,假如属性值是一个对象的引用,那么也会指向那个引用;
手写浅拷贝
function shallowClone(target){
let obj = {}
for(let key in target){
if (target.hasOwnProperty(key)) {
obj[key] = target[key]
}
}
return obj
}
深拷贝的实现方式
JSON.parse(JSON.stringify())函数库lodash的_.cloneDeep方法jQuery.extend()方法
手写递归实现深拷贝
基础版
function clone(target, map = new WeakMap()) {
// obj不是对象或者数组, 或者是null,直接返回
if (typeof target === 'object' && target !=null) {
// 判断数组和对象,初始化
const isArray = Array.isArray(target);
let cloneTarget = isArray ? [] : {};
// 处理循环引用:如var a = {};a.a = a;
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
for (const key in target) {
// 确保不是原型链上的属性
if(target.hasOwnProperty(key)){
// 递归
cloneTarget[key] = clone(target[key], map);
}
}
return cloneTarget;
} else {
return target;
}
}
惊艳版
// 可继续遍历的数据类型
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]';
//不可继续遍历的数据类型
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';
const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];
//工具函数-通用while循环(比 for in效率高)
function forEach(array, iteratee) {
let index = -1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
}
// 工具函数-判断是否为引用类型
function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}
// 工具函数- 获取实际类型
function getType(target) {
return Object.prototype.toString.call(target);
}
// 工具函数- 初始化被克隆的对象
function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
}
// 工具函数- 克隆symbol
function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe));
}
// 工具函数- 克隆正则
function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
}
// 工具函数- 克隆函数
function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
if (func.prototype) {
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return eval(funcString);
}
}
// 工具函数- 克隆不可遍历对象
function cloneOtherType(targe, type) {
const Ctor = targe.constructor;
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe);
case regexpTag:
return cloneReg(targe);
case symbolTag:
return cloneSymbol(targe);
case funcTag:
return cloneFunction(targe);
default:
return null;
}
}
function clone(target, map = new WeakMap()) {
// 克隆原始类型-原始类型直接返回
if (!isObject(target)) {
return target;
}
// 初始化-根据不同类型进行操作
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
} else {
return cloneOtherType(target, type);
}
// 防止循环引用:weak弱引用,易被回收
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
// 克隆set
if (type === setTag) {
target.forEach(value => {
cloneTarget.add(clone(value, map));
});
return cloneTarget;
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value, map));
});
return cloneTarget;
}
// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
}
module.exports = {
clone
};