js-challenges icon indicating copy to clipboard operation
js-challenges copied to clipboard

深拷贝

Open Sunny-117 opened this issue 2 years ago • 20 comments

const _completeDeepClone = (target, map = new WeakMap()) => {
  // 基本数据类型,直接返回
  if (typeof target !== 'object' || target === null) return target
  // 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
  const constructor = target.constructor
  if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target)
  // map标记每一个出现过的属性,避免循环引用
  if (map.get(target)) return map.get(target)
  map.set(target, true)
  const cloneTarget = Array.isArray(target) ? [] : {}
  for (prop in target) {
    if (target.hasOwnProperty(prop)) {
      cloneTarget[prop] = _completeDeepClone(target[prop], map)
    }
  }
  return cloneTarget
}

Sunny-117 avatar Nov 03 '22 08:11 Sunny-117

你map的value存的是true?

hnustwjj avatar Nov 03 '22 10:11 hnustwjj

正则...

bylvis avatar Nov 04 '22 13:11 bylvis

        let obj = {
            a: 1,
            b: 2,
            c: {
                c: 1,
                d: 2
            }
        }
        // 方法一
        let obj1 = JSON.parse(JSON.stringify(obj)) 
            // 测试用例
            // console.log(obj1);
            // obj1.b = 22
            // obj1.c.c = 11
            // console.log(obj);
        // 方法二
        function deepClone(obj) {
            let objClone = Array.isArray(obj) ? [] : {}
            for(let key of Object.keys(obj)) {
                objClone[key] = (typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
            }
            return objClone
        }
        console.log(deepClone(obj))

hannah-bingo avatar Jan 24 '23 03:01 hannah-bingo

export function objDeepCopy(source) {
  if (typeof source === "object") {
    var sourceCopy = source instanceof Array ? [] : {}
    for (var item in source) {
      if (!source[item]) {
        sourceCopy[item] = source[item]
      } else {
        sourceCopy[item] =
          typeof source[item] === "object"
            ? objDeepCopy(source[item])
            : source[item]
      }
    }
    return sourceCopy
  }
  return source
}

ppjiucai avatar Jan 31 '23 06:01 ppjiucai

深拷贝:只是针对Object 和 Array这样的引用数据类型的。 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象,是“值”而不是“引用”(不是分支)

浅拷贝的实现方式:

当对象object 只有一层的时候,是深拷贝 实现方式:

  • Object.assign({}, obj)
  • Array.prototype.concat() 或者 slice 都是浅复制

深拷贝的实现方式:

1. json.stringfiy 和 parse

let a = {
    name: 'ss',
}
let b = JSON.parse(JSON.stringify(a));

缺点:

如果obj里面有时间对象,转换过后,时间将只是字符串的形式 如果obj有RegExp, Error 对象,则序列化的结果将只得到空对象 如果obj里面有函数,undefined,序列化后的结果会把函数或undefined丢失 如果obj里有NaN,Infinity 和 -Infinity,则序列化的结果会变成null JSON.stringify() 只能序列化对象的可枚举的自由属性,例如 如果obj中的对象是由构造函数生成的,则使用JSON.parse(JSON.stringify),则会丢弃对象的constructor 如果对象中存在循环引用的情况也无法正确实现深拷贝

2. 手写递归深拷贝

const deepcolne = (target, map = new WeekMap()) =>{
    // 基本类型直接返回
    if(typeof target !== 'object' || target === null) return target;
    // 函数 正则 日期 ES6新对象, 执行构造
    const constructor = target.constructor;
    if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target);
    // map 标记每一个出现过的属性,避免循环引用
    if(map.get(target)) return map.get(target);
    map.set(target, true);
    const cloneTarget = Array.isArray(target) ? [] : {};
    for(const prop in target){
        cloneTarget[prop] = deepclone(target[prop], map);
    }
    return cloneTarget;
}

3. loadash函数库

let _ = require('loadash');
_.cloneDeep();

jlx2002 avatar Feb 03 '23 10:02 jlx2002

  1. 手写递归
function deepClone(origin) {
  const map = new WeakMap(); // 解决循环引用

  const clone = (obj) => {
    if (typeof obj !== 'object') {
      return obj;
    }

    if (map.has(obj)) {
      return map.get(obj);
    }

    let newObj = Array.isArray(obj) ? [] : {};

    map.set(obj, newObj);

    Object.keys(obj).forEach(key => {
        newObj[key] = clone(obj[key]);
    })

    return newObj;
  }
  return clone(origin);
}
  1. chrome98以上支持原生深拷贝方法
const newObj = structuredClone(obj);  // 不能拷贝函数

Tsuizen avatar Feb 07 '23 07:02 Tsuizen

// 判断数据类型
const isType = (val) => {
  return (type) => Object.prototype.toString.call(val) === `[object ${type}]`
}
function deepMerge(target) {
  const ret = Array.isArray(target) ? [] : {};
  for (let key in target) {
    if (target.hasOwnProperty(key)) {
      if (isType(target[key])('Object')) {
       // 判断对象的值是不是对象,如果是对象则进一步递归
        ret[key] = deepMerge(target[key])
      } else if (isType(target[key])('Array')) {
       // 如果是数组则进一步进行遍历数组,用一个新数组[]去添加值
        ret[key] = [].concat([...deepMerge(target[key])]);
      } else {
        ret[key] = target[key]; // 如果是基础数据类型,则直接赋值
      }
    }
  }
  return ret;
}

maicFir avatar Feb 13 '23 01:02 maicFir

解决循环引用,和拷贝不可枚举对象!!

/**
 * 深拷贝会拷贝不可枚举属性,浅拷贝不会
 * @param {*} target 
 * @param {*} map 避免循环引用
 */
function _deepClone(target, map = new Map()) {
  if(typeof target !== "object" || target === null)  return target;
  if(map.get(target)) return map.get(target); // 避免循环引用
  // 除了{}和[],new target.constructor(target)都可以重新开辟内存
  if(/^(Function|RegExp|Date|Set|Map)$/.test(target.constructor.name)) {
    const res = new target.constructor(target);
    map.set(target, res);
    return res;
  }
  const cloneTarget = Array.isArray(target) ? [] : {}
  Object.getOwnPropertySymbols(target).forEach(item => {
    cloneTarget[item] = _deepClone(target[item], map);
    map.set(target, cloneTarget[item]);
  })
  Object.getOwnPropertyNames(target).forEach(item => {
    if(!target.propertyIsEnumerable(item)) {
      Object.defineProperty(target, item, Object.getOwnPropertyDescriptor(target,item));
    } else {
      cloneTarget[item] = _deepClone(target[item], map);
      map.set(target, cloneTarget[item]);
    }
  })
  // Object.propertyIsEnumerable() 判断是否是不可枚举的属性值
  return cloneTarget;
}

const arr = [1,2,3]
const data = {
  o: arr,
  // a: function() {},
  b: {
    a: arr
  }
}
Object.defineProperty(data, 'c', { enumerable: false, value: 2 }) // 默认设置为不可读不可写

const res = _deepClone(data)
console.log(res)
console.log(arr == res.b.a)

zzyyhh22lx avatar Feb 15 '23 19:02 zzyyhh22lx

function deepCopy(obj) {
  // 确定需要复制的对象类型
  const type = Object.prototype.toString.call(obj).slice(8, -1);

  // 根据类型进行处理
  switch (type) {
    case "Object":
      const newObj = {};
      for (let key in obj) {
        newObj[key] = deepCopy(obj[key]);
      }
      return newObj;

    case "Array":
      const newArr = [];
      for (let i = 0; i < obj.length; i++) {
        newArr.push(deepCopy(obj[i]));
      }
      return newArr;

    case "Date":
      return new Date(obj.getTime());

    case "RegExp":
      return new RegExp(obj);

    default:
      return obj;
  }
}

kangkang123269 avatar Feb 25 '23 10:02 kangkang123269

if(map.get(target)) return map.get(target); 注意map.get(target) 返回的是啥

crazy23lt avatar Mar 02 '23 14:03 crazy23lt

function isObject(val) {
    return typeof val === "object" && val !== null;
}

// 难点如下:
// 1)注意收集 Symbol 属性
// 2) 注意可能会有循环引用,所以使用 map 记录,返回一个循环的指向即可,避免持续运行爆栈
// 3)注意一些特殊对象,Function,RegExp,Date,Map,Set这些,需要去构造新的实例
// 3)区分数组还是对象, 数组会有 ['0', '1', '2', 'length'] 这些属性,构造新的对象
function deepClone(obj, hash = new WeakMap()) {
    if (!isObject(obj)) return obj; // 不是对象不拷贝直接返回
    if (hash.has(obj)) { // 如果哈希表中有这个对象的记录,取出并返回
        return hash.get(obj);
    }

      // 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
    const constructor = obj.constructor; 
    if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(obj);
  
    let target = Array.isArray(obj) ? [] : {}; // 拷贝对象还是数组,创建新的内存地址
    hash.set(obj, target); // hash 表记录对象

    Reflect.ownKeys(obj).forEach((item) => {
        if (isObject(obj[item])) { // 子属性(包括Symbol)也是对象的话递归调用
            target[item] = deepClone(obj[item], hash);
        } else {
            target[item] = obj[item]; 
        }
    });

    return target;
}

XiaoY0324 avatar Mar 04 '23 03:03 XiaoY0324

let obj = {
  a: 1,
  b: 2,
  c: {
    c: 1,
    d: 2
  }
}

function deepClone(obj) {
  let clone = Array.isArray(obj) ? [] : {}
  for (let key of Object.keys(obj)) {
    if (typeof obj[key] === 'object') {
      clone[key] = deepClone(obj[key])
    } else {
      clone[key] = obj[key]
    }
  }
  return clone
}

let clone = deepClone(obj)
clone.a = 1000000000
console.log(obj);

Bbbtt04 avatar Mar 12 '23 05:03 Bbbtt04

没测试过,只求面试不要我运行

    /**
     * 基本数据类型是默认赋值等于拷贝,可直接返回
     * 如果是对象则递归
     * set Map Date RegExp Function 
    */
    function completeDeepClone(target){
        if(typeof target != 'object' || target == null)return target;
        const cloneTarget = {};
        const constructor = target.constructor;
        if(/^Set|Map|Date|RegExp|Function$/.test(target))return new constructor(target);
        for(let key in target){
            cloneTarget[key] = completeDeepClone(target[key]);
        }
        return cloneTarget;
    }

xun-zi avatar Mar 13 '23 09:03 xun-zi

const mydeepClone = (target, map = new WeakMap()) => {
  if (typeof target !== "object" || typeof target === "null") return target;
  const constructor = target.constructor;
  if (/^Date|Function|RegExp|Set|Map$/i.test(constructor.name)) {
    return new constructor(target);
  }
  const cloneTarget = Array.isArray(target) ? [] : {};
  if (map.get(target)) {
    return map.get(target);
  }
  map.set(target, cloneTarget);
  for (let key in target) {
    cloneTarget[key] = mydeepClone(target[key], map);
  }
  return cloneTarget;
};

const target = {
  field1: 1,
  field2: undefined,
  field3: {
    child: "child",
  },
  field4: [2, 4, 8],
};
target.target = target;
const t1 = mydeepClone(target);
target.field3.child = "CHILD";
console.log(t1, target);

YMnotafraid avatar Mar 20 '23 09:03 YMnotafraid

const _completeDeepClone = (target, map = new WeakMap()) => {
  // 基本数据类型,直接返回
  if (typeof target !== 'object' || target === null) return target
  // 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
  const constructor = target.constructor
  if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target)
  // map标记每一个出现过的属性,避免循环引用
  if (map.get(target)) return map.get(target)
  map.set(target, true)
  const cloneTarget = Array.isArray(target) ? [] : {}
  for (prop in target) {
    if (target.hasOwnProperty(prop)) {
      cloneTarget[prop] = _completeDeepClone(target[prop], map)
    }
  }
  return cloneTarget
}

如果Set Map里存的是对象的话,并没有深拷贝 可以用这个样例测试

let a = {
    a:1
}
let test = {
  map: new Map([['key1', 'value1'], ['key2', 'value2'], [a, a]]),
  set:new Set([a])
}
let result = _completeDeepClone(test)
a.a=3
console.log(result)

2239351258 avatar Mar 25 '23 09:03 2239351258

const obj = {
    name: 'xiaohei',
    bool: true,
    nul: null,
    undef: undefined,
    show: function () {
        console.log('show function');
    },
    num: 20,
    set: new Set([1, 2, 3]),
    map: new Map(),
    date: new Date(),
    reg: /.xml/g,
    info: {
        msg: 'old msg',
    },
    sym: Symbol('a'),
};
obj.obj = obj;
obj.map.set('mapKey', 'map-value');
obj.map.set('obj', obj);
const sym = Symbol('sym');
obj[sym] = 1;

const newObj = deepClone(obj);

function deepClone(target, hash = new WeakMap()) {
    if (target === null) return null;
    if (target instanceof Date) return new Date(target);
    if (target instanceof RegExp) return new RegExp(target);
    if (typeof target !== 'object') return target;
    if (hash.get(target)) return hash.get(target);

    if (target instanceof Set) {
        const set = new Set();
        hash.set(target, set);
        target.forEach((value) => {
            if (typeof value === 'object') {
                set.add(deepClone(value, hash));
            } else {
                set.add(value);
            }
        });

        return set;
    }

    if (target instanceof Map) {
        const map = new Map();
        hash.set(target, map);
        for (const [key, value] of target) {
            if (typeof value === 'object') {
                map.set(key, deepClone(value, hash));
            } else {
                map.set(key, value);
            }
        }

        return map;
    }

    const cloneObj = new target.constructor();
    hash.set(target, cloneObj);
    Reflect.ownKeys(target).forEach((key) => {
        cloneObj[key] = deepClone(target[key], hash);
    });

    return cloneObj;
}

ry09iu avatar Apr 07 '23 16:04 ry09iu

function deep(target, set = new WeakSet()) { let data = Array.isArray(target) ? [] : {}; const constructor = target.constructor; if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target); if (set.has(target)) return target; else set.add(target); Object.keys(target).forEach((key) => { let val = target[key]; if (typeof val === "object" && val !== null) { data[key] = deep(val, set); } else { data[key] = val; } }); return data; }

cscty avatar Jun 17 '23 13:06 cscty

const _completeDeepClone = (target, map = new WeakMap()) => { // 基本数据类型,直接返回 if (typeof target !== "object" || target === null) return target;

// 函数 正则 日期 ES6新对象,执行构造题,返回新的对象
const constructor = target.constructor;
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) return new constructor(target);

// 查看是否已经被map标记(出现循环引用)
if (map.get(target)) return map.get(target);

const cloneTarget = Array.isArray(target) ? [] : {};

// map记录当前对象已经创建,值为当前对象
map.set(target, cloneTarget);

for (prop in target) {
    if (target.hasOwnProperty(prop)) {
        cloneTarget[prop] = _completeDeepClone(target[prop], map);
    }
}
return cloneTarget;

};

skyler-developer avatar Mar 28 '24 07:03 skyler-developer


function deepClone(value, dep = new Set(), arrMap = new Map()) {
  if (Array.isArray(value)) {
    if (arrMap.has(value)) {
      return arrMap.get(value);
    }
    let newArr = Array.from(value);
    arrMap.set(value, newArr);
    return newArr.map((item) => {
      return deepClone(item, dep, arrMap);
    });
  }

  if (typeof value === "object" && value !== null && !dep.has(value)) {
    dep.add(value);
    const obj = {};
    for (const key in value) {
      Object.hasOwnProperty.call(value, key) &&
        (obj[key] = deepClone(value[key], dep, arrMap));
    }
    return obj;
  }
  return value;
}

JiangXinYu113 avatar Aug 22 '24 07:08 JiangXinYu113

function clone(target,deep = true,map = new WeakMap()){
    if(typeof target !== 'object' || target === null){
        return target
    }
    else if(typeof target === 'function'){
        if(!deep){
            return target
        }
        return (new Function(`return ${target}`))()
    }
    if(map.has(target)){
        return map.get(target)
    }
    else{
        map.set(target,target)
    }
    if(!deep){
        return target
    }
    const constructor = target.constructor;
    const reg = /^(RegExp|Date|Set|Map)$/
    if(reg.test(constructor.name)){
        return new constructor(target)
    }
    if(Array.isArray(target)){
        return target.map(item => clone(item,deep,map))
    }
    else{
        const result = {};
        for (const key in target) {
            if (Object.prototype.hasOwnProperty.call(target, key)) {
                result[key] = clone(target[key],deep,map)
            }
        }
        return result;
    }
}

jianxingyao avatar Sep 15 '24 09:09 jianxingyao