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;
}

 

posted @ 2019-04-26 18:15  谢书怜  阅读(2072)  评论(0编辑  收藏  举报