乞丐版深拷贝
- JSON.parse(JSON.stringify())
- 缺点:无法处理循环引用问题
const obj = {
name:'red',
age:18
}
obj.cir = obj
// 这时候的 obj其实是
// obj {
// name: "red",
// age: 18,
// cir: {
// name: "red",
// age: 18,
// cir: {
// name: "red",
// age: 18,
// cir:......
// }
// }
// };
JSON.parse(JSON.stringify(obj))
// 报错:Uncaught TypeError: Converting circular structure to JSON
我发现使用map也可以实现深拷贝
- 但是应该有很多种情况没有考虑,比如传进来的如果不是对象
function clone(source) {
const iterableArr = Object.entries(source);
return Object.fromEntries(new Map(iterableArr));
}
略微升级版
/**
* @des 深拷贝
* @param {*} source 要被克隆的对像
* @param {Map} map
* @return:
*/
function clone(source, map = new WeakMap()) {
if (typeof source === "object") {
let target = Array.isArray(source) ? [] : {};
if (map.get(source)) {
return map.get(source);
}
map.set(source, target);
for (let [key, value] of Object.entries(source)) {
target[key] = clone(value, map);
}
return target;
} else {
return source;
}
}
lodash的深拷贝+理解注释
import { isObject, isBuffer } from "util";
import copyArray from "./copyArray.js";
/* 用于编写用于克隆的位掩码 */
const CLONE_DEEP_FLAG = 1;
const CLONE_FLAT_FLAG = 2;
const CLONE_SYMBOLS_FLAG = 4;
/* `Object#toString` 的结果引用 */
const argsTag = "[object Arguments]";
const arrayTag = "[object Array]";
const boolTag = "[object Boolean]";
const dateTag = "[object Date]";
const errorTag = "[object Error]";
const mapTag = "[object Map]";
const numberTag = "[object Number]";
const objectTag = "[object Object]";
const regexpTag = "[object RegExp]";
const setTag = "[object Set]";
const stringTag = "[object String]";
const symbolTag = "[object Symbol]";
const weakMapTag = "[object WeakMap]";
const arrayBufferTag = "[object ArrayBuffer]";
const dataViewTag = "[object DataView]";
const float32Tag = "[object Float32Array]";
const float64Tag = "[object Float64Array]";
const int8Tag = "[object Int8Array]";
const int16Tag = "[object Int16Array]";
const int32Tag = "[object Int32Array]";
const uint8Tag = "[object Uint8Array]";
const uint8ClampedTag = "[object Uint8ClampedArray]";
const uint16Tag = "[object Uint16Array]";
const uint32Tag = "[object Uint32Array]";
/* 用于标识 `clone` 支持的 `toStringTag` 值 */
const cloneableTags = {};
cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[
dataViewTag
] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[
float64Tag
] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[
mapTag
] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[
setTag
] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[
uint8ClampedTag
] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
cloneableTags[errorTag] = cloneableTags[weakMapTag] = false;
/* 用于检查对象自身属性中是否有指定的属性 */
const hasOwnProperty = Object.prototype.hasOwnProperty;
/* 判断参数类型 */
function getTag(value) {
if (value == null) {
return value === undefined ? "[object Undefined]" : "[object Null]";
}
Object.prototype.toString.call(value);
}
/* 初始化一个数组 */
function initCloneArray(array) {
const { length } = array;
const result = new array.constructor(length);
// 添加由 `RegExp#exec` 分配的属性
// exec() 方法返回一个数组(包含额外的属性 index 和 input属性)
if (length && typeof array[0] === "string" && hasOwnProperty.call(array, "index")) {
result.index = array.index;
result.input = array.input;
}
return result;
}
/**
* @des 深克隆
* @param {*} value 要被克隆的值
* @param {number} bitmask 位掩码标识符
* 1 - 深克隆
* 2 - 展平属性
* 4 - 克隆symbol
* @param {Function} [customizer] 定制的克隆函数.
* @param {string} [key] `value`的键
* @param {Object} [object] `value`的父对象.
* @param {Object} [stack] 跟踪遍历的对象及其克隆副本
* @return:
*/
function baseClone(value, bitmask, customizer, key, object, stack) {
let result;
// CLONE_DEEP_FLAG => 1 => 0001
// CLONE_FLAT_FLAG => 2 => 0010
// CLONE_SYMBOLS_FLAG => 4 => 0100
// 若bitmask = 1, 则isDeep为true,其他为false
// 若bitmask = 3, 则isDeep,isFlat为true,isFull为false
const isDeep = bitmask & CLONE_DEEP_FLAG;
const isFlat = bitmask & CLONE_FLAT_FLAG;
const isFull = bitmask & CLONE_SYMBOLS_FLAG;
// 如果用户有定制克隆函数的话,使用用户自定函数进行克隆
if (customizer) {
// 若被克隆的`value`有父对象,则将 被克隆的`value`本身,他的`key`,他的父对象传给自定义克隆函数
// 若无,则仅将 `value` 传给定制函数
result = object ? customizer(value, key, object, stack) : customizer(value);
}
if (result !== undefined) {
return result;
}
// 若被克隆的`value`不是对象,直接返回值即可
if (!isObject(value)) {
return value;
}
const isArr = Array.isArray(value);
// function getTag(value) {
// if (value == null) {
// return value === undefined ? "[object Undefined]" : "[object Null]";
// }
// Object.prototype.toString.call(value);
// }
const tag = getTag(value);
if (isArr) {
// 初始化一个空数组
result = initCloneArray(value);
if (!isDeep) {
// 若不是深拷贝的话,调用数组拷贝方法
// 这个方法先创建一个和 `value` 长度一样的空数组,然后再循环将 `value` 的值赋给新的空数组
return copyArray(value, result);
}
} else {
// 如果被拷贝 `value` 不是数组,则按照下面的思路进行拷贝
const isFunc = typeof value === "function";
// 如果是 Buffer类型,则调用 `cloneBuffer` 方法对其克隆
if (isBuffer(value)) {
return cloneBuffer(value, isDeep);
}
// 如果Object#toString 检测的类型是 对象、参数、是函数但是不是对象
if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
result = isFlat || isFunc ? {} : initCloneObject(vlaue);
if (!isDeep) {
// TODO: 我有点懵了,上面的判断条件和是否深拷贝,为什么调用拷贝 Symbol的函数
return isFlat
? copySymbolsIn(value, copyObject(value, keysIn(value), result))
: copySymbols(value, Object.assign(result, value));
}
} else {
if (isFunc || !cloneableTags[tag]) {
// 如果是函数或者是不能克隆的类型
// TODO: ???没有父对象的话返回 {} ??
return object ? value : {};
}
}
// 检查循环引用并且返回其对应的克隆
stack || (stack = new stack());
// 如果栈里面已经有这个对象的话,就直接返回栈里保存的值,
// 上面的简单版本这里的处理是使用了 `weakMap` 存储
const stacked = stack.get(value);
if (stacked) {
return stacked;
}
stack.set(value, result);
if (tag == mapTag) {
value.forEach(subValue => {
result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
});
return result;
}
if (tag == setTag) {
value.forEach(subValue => {
result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
});
return result;
}
// 类型化数组
if (isTypeArray(value)) {
return result;
}
// TODO: 这里完全看不懂了
const keysFunc = isFull ? (isFlat ? getAllKeysIn : getAllKeys) : isFlat ? keysIn : keys;
const props = isArr ? undefined : keysFunc(value);
arrayEach(props || value, (subValue, key) => {
if (props) {
key = subValue;
subValue = value[key];
}
// Recursively populate clone (susceptible to call stack limits).
assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
});
return result;
}
}
网友评论