js 对象深拷贝函数
// 深拷贝函数封装1 function deepCopy(obj) { // 根据obj的类型判断是新建一个数组还是对象 let newObj = Array.isArray(obj)? [] : {}; // 判断传入的obj存在,且类型为对象 if (obj && typeof obj === 'object') { for(key in obj) { // 如果obj的子元素是对象,则进行递归操作 if(obj[key] && typeof obj[key] ==='object') { newObj[key] = deepCopy(obj[key]) } else { // // 如果obj的子元素不是对象,则直接赋值 newObj[key] = obj[key] } } } return newObj // 返回新对象 } // 对象的深拷贝 let obj1 = { a: '1', b: '2', c: { name: 'Demi' } } let obj2 = deepCopy(obj1) //将obj1的数据拷贝到obj2 obj2.c.name = 'wilsunson' console.log(obj1) // {a: "1", b: "2", c: {name: 'wss'}} console.log(obj2) // {a: "1", b: "2", c: {name: 'wilsunson'}}
// 方法2
const objDeepClone = obj => { return clone(obj) } const isType = (obj, type) => { if (typeof obj !== 'object') return false; // 判断数据类型的经典方法: const typeString = Object.prototype.toString.call(obj); let flag; switch (type) { case 'Array': flag = typeString === '[object Array]'; break; case 'Date': flag = typeString === '[object Date]'; break; case 'RegExp': flag = typeString === '[object RegExp]'; break; default: flag = false; } return flag; }; /** * deep clone * @param {[type]} parent object 需要进行克隆的对象 * @return {[type]} 深克隆后的对象 */ const clone = parent => { // 维护两个储存循环引用的数组 const parents = [] const children = [] const _clone = parent => { if (parent === null) return null if (typeof parent !== 'object') return parent let child, proto if (isType(parent, 'Array')) { // 对数组做特殊处理 child = [] } else if (isType(parent, 'RegExp')) { // 对正则对象做特殊处理 child = new RegExp(parent.source, getRegExp(parent)) if (parent.lastIndex) child.lastIndex = parent.lastIndex } else if (isType(parent, 'Date')) { // 对Date对象做特殊处理 child = new Date(parent.getTime()) } else { // 处理对象原型 proto = Object.getPrototypeOf(parent) // 利用Object.create切断原型链 child = Object.create(proto) } // 处理循环引用 const index = parents.indexOf(parent) if (index !== -1) { // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象 return children[index] } parents.push(parent) children.push(child) for (const i in parent) { // 递归 child[i] = _clone(parent[i]) } return child } return _clone(parent) } console.log(objDeepClone({ name: '张三', age: 23, obj: { name: '李四', age: 46}, arr:[1,2,3] })) /
更简单的??
function copy(obj, appeard = new Map()) { if (!(obj instanceof Object)) return obj // 原始数据类型 if (appread.has(obj)) return appeard.get(obj) // 重复引用 let result = Array.isArray(obj) ? [] : {} appeard.set(obj, result) // 将对象存入map // 遍历所有属性进行递归拷贝 ;[...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)].forEach(key => { result[key] = copy(obj[key], appeard) }) return result }
高阶深拷贝(转于:https://cloud.tencent.com/developer/article/1540790)
clone & cloneDeep(考虑各种对象)
_.clone(value)
创建一个 value 的浅拷贝。_.cloneDeep(value)
创建一个 value 的深拷贝。- 注意: 这个方法参考自 structured clone algorithm 以及支持 arrays、array buffers、 booleans、 date objects、maps、 numbers, Object objects, regexes, sets, strings, symbols, 以及 typed arrays。 参数对象的可枚举属性会拷贝为普通对象。 一些不可拷贝的对象,例如error objects、functions, DOM nodes, 以及 WeakMaps 会返回空对象。
- 源码中,clone是
baseClone(value, CLONE_SYMBOLS_FLAG)
,cloneDeep是baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG)
,此外,cloneDeepWith
函数也是baseClone
实现,此时baseClone
基于还多了第三个参数customizer
,是一个函数(customizer(value, key)
),对深拷贝前的值做预处理。下面我们要实现baseClone
方法 - 难度系数: ★★★★★★★★
主要代码:
/* 常量定义 */ const CLONE_DEEP_FLAG = 1; const CLONE_FLAT_FLAG = 2; const CLONE_SYMBOLS_FLAG = 4; globalThis.Buffer = globalThis.Buffer || undefined; const argsTag = "[object Arguments]", arrayTag = "[object Array]", boolTag = "[object Boolean]", dateTag = "[object Date]", errorTag = "[object Error]", funcTag = "[object Function]", genTag = "[object GeneratorFunction]", mapTag = "[object Map]", numberTag = "[object Number]", objectTag = "[object Object]", regexpTag = "[object RegExp]", setTag = "[object Set]", stringTag = "[object String]", symbolTag = "[object Symbol]", weakMapTag = "[object WeakMap]"; const arrayBufferTag = "[object ArrayBuffer]", dataViewTag = "[object DataView]", float32Tag = "[object Float32Array]", float64Tag = "[object Float64Array]", int8Tag = "[object Int8Array]", int16Tag = "[object Int16Array]", int32Tag = "[object Int32Array]", uint8Tag = "[object Uint8Array]", uint8ClampedTag = "[object Uint8ClampedArray]", uint16Tag = "[object Uint16Array]", uint32Tag = "[object Uint32Array]"; /* 初始化系列 */ // 初始化数组 function initCloneArray(array) { const length = array.length; const result = new array.constructor(length); // 正则match返回 if ( length && typeof array[0] == "string" && hasOwnProperty.call(array, "index") ) { result.index = array.index; result.input = array.input; } return result; } // 初始化普通对象 // 是否是原型对象 function isPrototype(value) { return value === (value.constructor.prototype || Object.prototype); } function initCloneObject(object) { // 不是原型对象,那就Object.create保持继承关系 // 是原型对象,那就直接返回一个空对象 return typeof object.constructor == "function" && !isPrototype(object) ? Object.create(Object.getPrototypeOf(object)) : {}; } // 获取[Object xxx] function getTag(v) { return Object.prototype.toString.call(v); } // 普通获取key function keys(object) { return Object.keys(object); } // 普通获取key,包含symbol key function getAllKeys(object) { // getOwnPropertySymbols会把不能枚举的都拿到 return [ ...Object.getOwnPropertySymbols(object).filter(key => object.propertyIsEnumerable(key) ), ...Object.keys(object) ]; } // 获取原型链上的key function keysIn(object) { const res = []; for (const key in object) { // 拷贝所有的属性(除了最大的原型对象) if ( key !== "constructor" || (!isPrototype(object) && object.hasOwnProperty(key)) ) { result.push(key); } } return res; } function getAllKeysIn(object) { const res = []; // in拿不到symbol key for (const key in object) { // 拷贝所有的属性(除了最大的原型对象) if ( key !== "constructor" || (!isPrototype(object) && object.hasOwnProperty(key)) ) { result.push(key); } } let temp = object; // 逐层获取symbol key while (temp) { res.push( ...Object.getOwnPropertySymbols(object).filter(key => object.propertyIsEnumerable(key) ) ); temp = Object.getPrototypeOf(object); } return res; } /* 克隆系列 */ // 克隆Buffer const allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined; function cloneBuffer(buffer, isDeep) { if (isDeep) { return buffer.slice(); // 深拷贝 } const length = buffer.length; const result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); // 为什么这就是浅拷贝呢? // 其实和const o = { b: 1 }的浅拷贝是const a = new Object(); a.b = o.b同样的道理 buffer.copy(result); return result; } // 克隆ArrayBuffer function cloneArrayBuffer(arrayBuffer) { // 先new一个一样长度的 const result = new arrayBuffer.constructor(arrayBuffer.byteLength); // 使用Uint8Array操作ArrayBuffer,重新set一次 new Uint8Array(result).set(new Uint8Array(arrayBuffer)); // 或者使用DataView // new DataView(result).setUint8(new Uint8Array(arrayBuffer)); return result; } // 克隆dataview function cloneDataView(dataView, isDeep) { // 先把buffer拷贝 const buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; // new的时候,传入拷贝过的buffer return new dataView.constructor( buffer, dataView.byteOffset, dataView.byteLength ); } // 克隆类型化数组 function cloneTypedArray(typedArray, isDeep) { const buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; return new typedArray.constructor( buffer, typedArray.byteOffset, typedArray.length ); } // 克隆正则对象 function cloneRegExp(regexp) { const result = new regexp.constructor(regexp.source, /\w*$/.exec(regexp)); result.lastIndex = regexp.lastIndex; // 有g的时候的坑 return result; } // 一些对象的初始化或者克隆 function initCloneByTag(object, tag, isDeep) { const Ctor = object.constructor; switch (tag) { case arrayBufferTag: return cloneArrayBuffer(object); case boolTag: case dateTag: return new Ctor(+object); case dataViewTag: return cloneDataView(object, isDeep); case float32Tag: case float64Tag: case int8Tag: case int16Tag: case int32Tag: case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: return cloneTypedArray(object, isDeep); case mapTag: return new Ctor(); case numberTag: case stringTag: return new Ctor(object); case regexpTag: return cloneRegExp(object); case setTag: return new Ctor(); case symbolTag: return Symbol.prototype.valueOf ? Object(object.valueOf()) : {}; } }
baseclone 代码:
function baseClone(value, bitmask, customizer, key, object, cache = new Set()) { const isDeep = bitmask & CLONE_DEEP_FLAG; // 是否深拷贝 const isFlat = bitmask & CLONE_FLAT_FLAG; // 是否拷贝原型链上的属性 const isFull = bitmask & CLONE_SYMBOLS_FLAG; // 是否拷贝symbol key const type = typeof value; const isArr = Array.isArray(value); let result; // cloneWith会用到的customizer if (customizer) { // object是递归带过来的 result = object ? customizer(value, key, object, cache) : customizer(value); } if (result !== undefined) { return result; } // 不是复杂类型直接返回 if (!(value !== null && (type === "object" || type === "function"))) { return value; } if (isArr) { // 克隆数组 result = initCloneArray(value); if (!isDeep) { // 浅拷贝,直接连上去 return result.concat(value); } } else { // 克隆其他 const tag = getTag(value); // 是不是函数,按照规范,函数不克隆 const isFunc = tag == funcTag || tag == genTag; // 克隆Buffer if (Buffer && Buffer.isBuffer(value)) { return cloneBuffer(value, isDeep); } // 普通对象、argument、函数 if (tag == objectTag || tag == argsTag || (isFunc && !object)) { // 初始化对象 result = isFlat || isFunc ? {} : initCloneObject(value); // 浅拷贝 if (!isDeep) { // 是否获取原型链上的symbol key和普通key const getKeysFunc = isFlat ? getAllKeysIn : getAllKeys; return getKeysFunc(value).reduce((acc, shallowCopyKey) => { // 一个个赋值 acc[shallowCopyKey] = value[shallowCopyKey]; return acc; }, {}); } } else { // 不能拷贝的对象就放弃 if (!cloneableTags[tag]) { return object ? value : {}; } // arrayBuffer、typedarray、dataView、regexp、Object{[基本数据类型]}的拷贝 // set、map在这里只是初始化一个空的 result = initCloneByTag(value, tag, isDeep); } } // 检查环引用 const cacheed = cache.has(value); if (cacheed) { return cacheed; } cache.add(value, result); // set和map,一个个来 if (isSet(value)) { value.forEach(subValue => { result.add( baseClone(subValue, bitmask, customizer, subValue, value, cache) ); }); } else if (isMap(value)) { value.forEach((subValue, key) => { result.set( key, baseClone(subValue, bitmask, customizer, key, value, cache) ); }); } // 获取key的函数 const keysFunc = isFull ? isFlat ? getAllKeysIn : getAllKeys : isFlat ? keysIn : keys; // 对象的属性,只有普通对象有props const props = isArr ? undefined : keysFunc(value); (props || value).forEach((subValue, key) => { let newKey = key; // 数组的index或者对象的key let newValue = subValue; // subValue本来是所拷贝的对象里面的key或者数组的一个元素值 // 是对象的时候 if (props) { newKey = newValue; // 如果是对象,新的key即是forEach第一个参数 newValue = value[newKey]; // 所拷贝的对象里面的key对应的value } // 赋值,递归还把当前的一些值带下去(value、cache、newKey) result[newKey] = baseClone( newValue, bitmask, customizer, newKey, value, cache ); }); return result; }