Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

深拷贝 #58

Open
Sunny-117 opened this issue Nov 3, 2022 · 18 comments
Open

深拷贝 #58

Sunny-117 opened this issue Nov 3, 2022 · 18 comments

Comments

@Sunny-117
Copy link
Owner

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
}
@hnustwjj
Copy link

hnustwjj commented Nov 3, 2022

你map的value存的是true?

@bylvis
Copy link

bylvis commented Nov 4, 2022

正则...

@hannah-bingo
Copy link
Contributor

        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))

@ppjiucai
Copy link

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
}

@jlx2002
Copy link
Contributor

jlx2002 commented Feb 3, 2023

深拷贝:只是针对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();

@Tsuizen
Copy link

Tsuizen commented Feb 7, 2023

  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);  // 不能拷贝函数

@maicFir
Copy link

maicFir commented Feb 13, 2023

// 判断数据类型
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;
}

@zzyyhh22lx
Copy link

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

/**
 * 深拷贝会拷贝不可枚举属性,浅拷贝不会
 * @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)

@kangkang123269
Copy link

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

@crazy23lt
Copy link

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

@XiaoY0324
Copy link

XiaoY0324 commented Mar 4, 2023

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

@Bbbtt04
Copy link

Bbbtt04 commented Mar 12, 2023

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

@xun-zi
Copy link

xun-zi commented Mar 13, 2023

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

    /**
     * 基本数据类型是默认赋值等于拷贝,可直接返回
     * 如果是对象则递归
     * 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;
    }

@YMnotafraid
Copy link

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

@2239351258
Copy link

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)

@ry09iu
Copy link

ry09iu commented Apr 7, 2023

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

@cscty
Copy link

cscty commented Jun 17, 2023

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

@skyler-developer
Copy link

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;

};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests