Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 95 题:模拟实现一个深拷贝,并考虑对象相互引用以及 Symbol 拷贝的情况

Open yygmind opened this issue 5 years ago • 47 comments

yygmind avatar Jun 28 '19 00:06 yygmind



function deepClone(obj, hash = new WeakMap()) {
    if (hash.has(obj)) return obj;
    var cobj;
    // null
    if (obj === null) { return obj }
    let t = typeof obj;

    // 基本类型
    switch (t) {
        case 'string':
        case 'number':
        case 'boolean':
        case 'undefined':
            return obj;
    }

    // 数组
    if (Array.isArray(obj)) {
        cobj = [];
        obj.forEach((c, i) => { cobj.push(deepClone(obj[i])) });
    } else {
        cobj = {};
        // object // symbol
        if (Object.prototype.toString.call(obj) === "[object Object]") {
            Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)).forEach(c => {
                hash.set(obj, obj);
                cobj[c] = deepClone(obj[c], hash);
            });
        } else {
            //内置Object
            cobj = obj;
        }
    }
    return cobj;
}

image

不知道有没有漏的。。

kungithub avatar Jun 28 '19 03:06 kungithub

一个不考虑其他数据类型的公共方法,基本满足大部分场景

function deepCopy(target, cache = new Set()) {
  if (typeof target !== 'object' || cache.has(target)) {
    return target
  }
  if (Array.isArray(target)) {
    target.map(t => {
      cache.add(t)
      return t
    })
  } else {
    return [...Object.keys(target), ...Object.getOwnPropertySymbols(target)].reduce((res, key) => {
      cache.add(target[key])
      res[key] = deepCopy(target[key], cache)
      return res
    }, target.constructor !== Object ? Object.create(target.constructor.prototype) : {})
  }
}

主要问题是

  1. symbol作为key,不会被遍历到,所以stringify和parse是不行的
  2. 有环引用,stringify和parse也会报错

我们另外用getOwnPropertySymbols可以获取symbol key可以解决问题1,用集合记忆曾经遍历过的对象可以解决问题2。当然,还有很多数据类型要独立去拷贝。比如拷贝一个RegExp,lodash是最全的数据类型拷贝了,有空可以研究一下

另外,如果不考虑用symbol做key,还有两种黑科技深拷贝,可以解决环引用的问题,比stringify和parse优雅强一些

function deepCopyByHistory(target) {
  const prev = history.state
  history.replaceState(target, document.title)
  const res = history.state
  history.replaceState(prev, document.title)
  return res
}

async function deepCopyByMessageChannel(target) {
  return new Promise(resolve => {
    const channel = new MessageChannel()
    channel.port2.onmessage = ev => resolve(ev.data)
    channel.port1.postMessage(target)
  }).then(data => data)
}

无论哪种方法,它们都有一个共性:失去了继承关系,所以剩下的需要我们手动补上去了,故有Object.create(target.constructor.prototype)的操作

lhyt avatar Jun 28 '19 06:06 lhyt

  const symbolName = Symbol();
  const obj = {
    objNumber: new Number(1),
    number: 1,
    objString: new String('ss'),
    string: 'stirng',
    objRegexp: new RegExp('\\w'),
    regexp: /w+/g,
    date: new Date(),
    function: function () {},
    array: [{a: 1}, 2],
    [symbolName]: 111
  }
  obj.d = obj;

  const isObject = obj => obj !== null && (typeof obj === 'object' || typeof obj === 'function');
  const isFunction = obj => typeof obj === 'function'
  function deepClone (obj, hash = new WeakMap()) {
    if (hash.get(obj)) {
      // 环处理
      return hash.get(obj);
    }
    if (!isObject(obj)) {
      return obj;
    }

    if (isFunction(obj)) {
      // function返回原引用
      return obj;
    }

    let cloneObj;

    const Constructor = obj.constructor;

    switch (Constructor) {
      case Boolean:
      case Date:
        return new Date(+obj);
      case Number:
      case String:
      case RegExp:
        return new Constructor(obj);
      default:
        cloneObj = new Constructor();
        hash.set(obj, cloneObj);
    }

    [...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)].forEach(k => {
      cloneObj[k] = deepClone(obj[k], hash);
    })
    return cloneObj;
  }
  

  const o = deepClone(obj)
  console.log(o.objNumber === obj.objNumber);
  console.log(o.number === obj.number);
  console.log(o.objString === obj.objString);
  console.log(o.string === obj.string);
  console.log(o.objRegexp === obj.objRegexp);
  console.log(o.regexp === obj.regexp);
  console.log(o.date === obj.date);
  console.log(o.function === obj.function);
  console.log(o.array[0] === obj.array[0]);
  console.log(o[symbolName] === obj[symbolName]);

dongj0316 avatar Jun 29 '19 08:06 dongj0316

一个不考虑其他数据类型的公共方法,基本满足大部分场景

function deepCopy(target, cache = new Set()) {
  if (typeof target !== 'object' || cache.has(target)) {
    return target
  }
  if (Array.isArray(target)) {
    target.map(t => {
      cache.add(t)
      return t
    })
  } else {
    return [...Object.keys(target), ...Object.getOwnPropertySymbols(target)].reduce((res, key) => {
      cache.add(target[key])
      res[key] = deepCopy(target[key], cache)
      return res
    }, target.constructor !== Object ? Object.create(target.constructor.prototype) : {})
  }
}

主要问题是

  1. symbol作为key,不会被遍历到,所以stringify和parse是不行的
  2. 有环引用,stringify和parse也会报错

我们另外用getOwnPropertySymbols可以获取symbol key可以解决问题1,用集合记忆曾经遍历过的对象可以解决问题2。当然,还有很多数据类型要独立去拷贝。比如拷贝一个RegExp,lodash是最全的数据类型拷贝了,有空可以研究一下

另外,如果不考虑用symbol做key,还有两种黑科技深拷贝,可以解决环引用的问题,比stringify和parse优雅强一些

function deepCopyByHistory(target) {
  const prev = history.state
  history.replaceState(target, document.title)
  const res = history.state
  history.replaceState(prev, document.title)
  return res
}

async function deepCopyByMessageChannel(target) {
  return new Promise(resolve => {
    const channel = new MessageChannel()
    channel.port2.onmessage = ev => resolve(ev.data)
    channel.port1.postMessage(target)
  }).then(data => data)
}

无论哪种方法,它们都有一个共性:失去了继承关系,所以剩下的需要我们手动补上去了,故有Object.create(target.constructor.prototype)的操作

有两个问题:

  1. 如果 target 是一个数组,拷贝结果没有返回
  2. 如果 target 是一个函数,函数没有被深拷贝

lovelope avatar Jul 02 '19 12:07 lovelope

一个不考虑其他数据类型的公共方法,基本满足大部分场景

function deepCopy(target, cache = new Set()) {
  if (typeof target !== 'object' || cache.has(target)) {
    return target
  }
  if (Array.isArray(target)) {
    target.map(t => {
      cache.add(t)
      return t
    })
  } else {
    return [...Object.keys(target), ...Object.getOwnPropertySymbols(target)].reduce((res, key) => {
      cache.add(target[key])
      res[key] = deepCopy(target[key], cache)
      return res
    }, target.constructor !== Object ? Object.create(target.constructor.prototype) : {})
  }
}

主要问题是

  1. symbol作为key,不会被遍历到,所以stringify和parse是不行的
  2. 有环引用,stringify和parse也会报错

我们另外用getOwnPropertySymbols可以获取symbol key可以解决问题1,用集合记忆曾经遍历过的对象可以解决问题2。当然,还有很多数据类型要独立去拷贝。比如拷贝一个RegExp,lodash是最全的数据类型拷贝了,有空可以研究一下 另外,如果不考虑用symbol做key,还有两种黑科技深拷贝,可以解决环引用的问题,比stringify和parse优雅强一些

function deepCopyByHistory(target) {
  const prev = history.state
  history.replaceState(target, document.title)
  const res = history.state
  history.replaceState(prev, document.title)
  return res
}

async function deepCopyByMessageChannel(target) {
  return new Promise(resolve => {
    const channel = new MessageChannel()
    channel.port2.onmessage = ev => resolve(ev.data)
    channel.port1.postMessage(target)
  }).then(data => data)
}

无论哪种方法,它们都有一个共性:失去了继承关系,所以剩下的需要我们手动补上去了,故有Object.create(target.constructor.prototype)的操作

有两个问题:

  1. 如果 target 是一个数组,拷贝结果没有返回
  2. 如果 target 是一个函数,函数没有被深拷贝

数组的确是我忘了写return了。然后拷贝函数这种操作平时真不会有人做。如果实在是要拷贝,除了简单的function.toString和正则匹配外,还要考虑箭头函数、参数默认值、换行、this、函数名字

lhyt avatar Jul 02 '19 13:07 lhyt

  1. 如果obj是null, 或者不是函数也不是object(即为包括Symbol在内的基本类型)则直接返回obj;
  2. 如果obj是Date或RegExp就返回对应的新实例;
  3. 在map中查找,找到则返回;
  4. 以上都不是,则通过new obj.constructor()eval(obj.toString())创建一个新实例temp,并保存进map,通过Object.getOwnPropertyNamesObject.getOwnPropertySymbols遍历obj的所有属性名,递归调用deepClone完成temp上所有属性的声明和赋值,最后返回temp
function deepClone(obj, map = new WeakMap()) {
  const type = typeof obj;

  if (obj === null || type !== 'function' && type !== 'object') return obj;
  if (obj instanceof Date) return Date(obj);
  if (obj instanceof RegExp) return RegExp(obj);
  if (map.has(obj)) return map.get(obj);

  const temp = type === 'function' ? eval(obj.toString()) : new obj.constructor();
  map.set(obj, temp);

  Object.getOwnPropertyNames(obj)
    .concat(Object.getOwnPropertySymbols(obj))
    .forEach((i) => {
      temp[i] = deepClone(obj[i], map);
    });

  return temp;
}

函数拷贝的情况太复杂了,所以就直接用了eval(obj.toString())

Zousdie avatar Jul 07 '19 09:07 Zousdie

写个不用递归用循环的方式实现的版本吧


const getType = obj => Object.prototype.toString.call(obj).match(/\[object\s(.*)]/)[1]

function deepClone(obj) {
    let res = {}
    let stack = []
    let root = {
        parent: obj,
        prop: null,
        data: res
    }
    let wm = new WeakMap()
    stack.push(root)

    while (stack.length) {
        let item = stack.pop()
        Reflect.ownKeys(item.parent).forEach(key => {
            if (wm.get(item.parent[key])) {
                item.data[key] = wm.get(item.parent[key])
                return
            }
            switch (getType(item.parent[key])) {
                case 'Object': {
                    item.data[key] = {}
                    stack.push({
                        parent: item.parent[key],
                        prop: key,
                        data: item.data[key]
                    })
                    wm.set(item.parent[key], item.parent[key])
                    break
                }
                case 'Array': {
                    item.data[key] = []
                    stack.push({
                        parent: item.parent[key],
                        prop: key,
                        data: item.data[key]
                    })
                    wm.set(item.parent[key], item.parent[key])
                    break
                }
                case 'Date': {
                    item.data[key] = new Date(item.parent[key])
                    break
                }
                case 'RegExp': {
                    item.data[key] = new RegExp(item.parent[key])
                    break
                }
                default: {
                    item.data[key] = item.parent[key]
                }
            }
        })
    }

    return res
}


let obj = {
    num: 0,
    str: '',
    boolean: true,
    unf: undefined,
    nul: null,
    obj: {
        name: '我是一个对象',
        id: 1,
        qwe: {
            a: 1
        }
    },
    arr: [0, 1, 2, {b: 2}],
    date: new Date(0),
    reg: /我是一个正则/ig,
    [Symbol('1')]: 1,
    func() {
        console.log(123)
    }
};

obj.loop = obj

let cloneObj = deepClone(obj);

console.log('obj', obj);
console.log('cloneObj', cloneObj);

// 对比两个对象引用类型的值是相同
Object.keys(cloneObj).filter(key => key !== 'nul').forEach(key => {
    if (typeof cloneObj[key] === 'object' || typeof cloneObj[key] === 'function') {
        console.log(`${key}相同吗? `, cloneObj[key] === obj[key])
    }
})

函数拷贝不了,还有一些奇奇怪怪的引用类型也拷贝不了,一般情况应该没啥问题,其实拷贝函数有一种思路是用AST(手动狗头)

yeyan1996 avatar Jul 09 '19 02:07 yeyan1996

function deepClone(obj, hash = new WeakMap()) {
    if (hash.has(obj)) return obj;
    var cobj;
    // null
    if (obj === null) { return obj }
    let t = typeof obj;

    // 基本类型
    switch (t) {
        case 'string':
        case 'number':
        case 'boolean':
        case 'undefined':
            return obj;
    }

    // 数组
    if (Array.isArray(obj)) {
        cobj = [];
        obj.forEach((c, i) => { cobj.push(deepClone(obj[i])) });
    } else {
        cobj = {};
        // object // symbol
        if (Object.prototype.toString.call(obj) === "[object Object]") {
            Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)).forEach(c => {
                hash.set(obj, obj);
                cobj[c] = deepClone(obj[c], hash);
            });
        } else {
            //内置Object
            cobj = obj;
        }
    }
    return cobj;
}

var a=[]; a.push(a); deepClone(a);

komaedaXnagito avatar Jul 11 '19 07:07 komaedaXnagito

野路子

缺点:无法拷贝函数 、Symbol

const deepClone = function (obj) {
    var str = JSON.stringify(obj);
    return JSON.parse(str);
}

richard1015 avatar Jul 22 '19 09:07 richard1015

Symbol 不是独一无二的吗?还能拷贝?

NathanHan1 avatar Jul 23 '19 04:07 NathanHan1

函数为什么要拷贝?函数不是用来复用的吗?

NathanHan1 avatar Jul 23 '19 04:07 NathanHan1

image @lhyt

Gemiry avatar Jul 25 '19 09:07 Gemiry

function clone(param) {
    // 数组 、 对象 、 普通值
    let res = null
    let type = Object.prototype.toString.call(param)
    if (type === '[object Object]') {
      res = {}
      for (const key in param) {
        res[key] = clone(param[key])
      }
    } else if (type === '[object Array]') {
      res = []
      param.forEach((item, index) => {
        res[index] = clone(item)
      })
    } else {
      res = param
    }
    return res
  }

  let o = { fn: () => { }, name: 1, o: { a: [1], name: 2, fn: () => { } }, arr: [1, 2, 3], s: Symbol() }
  let o2 = clone(o)
  o2.name = 200
  console.log(o , o2)

jiao2563719877 avatar Jul 30 '19 07:07 jiao2563719877

分享一个来自 Vuex 的 deepCopy 解决了循环引用,cache 存储所有嵌套 obj 及其 copy 副本,以 cache 中是否有某个嵌套 obj 来判断是否循环引用,有则返回 引用的 copy

export function deepCopy (obj, cache = []) {
  // just return if obj is immutable value
  if (obj === null || typeof obj !== 'object') {
    return obj
  }

  // if obj is hit, it is in circular structure
  const hit = find(cache, c => c.original === obj)
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ? [] : {}
  // put the copy into cache at first
  // because we want to refer it in recursive deepCopy
  cache.push({
    original: obj,
    copy
  })

  Object.keys(obj).forEach(key => {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}

JackFGreen avatar Aug 01 '19 08:08 JackFGreen

为什么没人用getOwnPropertyDescriptors

zzNire avatar Aug 14 '19 09:08 zzNire

@lhyt 的

function deepCopy(target, cache = new Set()) {
  if (typeof target !== 'object' || cache.has(target)) {
    return target
  }
  if (Array.isArray(target)) {
    target.map(t => {
      cache.add(t)
      return t
    })
  } else {
    return [...Object.keys(target), ...Object.getOwnPropertySymbols(target)].reduce((res, key) => {
      cache.add(target[key])
      res[key] = deepCopy(target[key], cache)
      return res
    }, target.constructor !== Object ? Object.create(target.constructor.prototype) : {})
  }
}

在这种情况下有问题

let ooo = {
    a: {}
};

ooo.a.c = ooo;

let eee = deepCopy(ooo)
console.log(eee.a.c === eee);
console.log(eee.a.c === ooo);

感觉环状数据之前这个里面 https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/10 处理的方法可以

yft avatar Aug 20 '19 12:08 yft

看大家都写的好复杂,我来一个简单点的吧。

const pub = new (class {
  // static obj = {};
  constructor() {}
  deepCopy(obj) {
    let result = Array.isArray(obj) ? [] : {};
    // 获取到当前层对象的所有属性。
    let ownProperty = [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)];
    for (let i in ownProperty) {
      if (obj.hasOwnProperty(ownProperty[i])) {
        // console.log(ownProperty[i], ':', obj[ownProperty[i]]);
        if (typeof obj[ownProperty[i]] === 'object' && obj[ownProperty[i]] != null) {
          result[ownProperty[i]] = this.deepCopy(obj[ownProperty[i]]);
        } else {
          result[ownProperty[i]] = obj[ownProperty[i]];
        }
      }
    }
    return result;
  }
})();

const HEAD = Symbol('头顶');
const HAT = Symbol('帽子');
const CAUSE = Symbol('原因');
// 新建一个魔性的对象。
let obj = {
  origin: { name: '小明', [HEAD]: '🍃' },
  [HAT]: { [CAUSE]: 'wife', color: 'green', background: '🌾', num: [1, 2, 3, 4, 5, 6, 7, 8, 9, [Infinity]] },
  move: function() {}
};

// 接下来对这个魔性的对象进行深拷贝,太残忍了。
let objCopy = pub.deepCopy(obj);

// 验证
obj[HAT].num[1] = 0;
console.log(obj, objCopy);
console.log('obj:', obj[HAT].num[1], ', objCopy:', objCopy[HAT].num[1]); // obj: 0 , objCopy: 2

yaodongyi avatar Sep 20 '19 02:09 yaodongyi

不考虑正则、函数等奇怪类型的拷贝,满足大多数深度拷贝需求 定制需求如下: 1、循环引用 2、Symbol 类型拷贝

function deepClone(val,map = new WeakMap()){
    if(val === null || typeof val !=='object') return val;
    //循环引用
    if(map.has(val)) return map.get(val);
    let clone = Array.isArray(val) ? [] : {};
    map.set(val,clone);
    // 获取对象中所有的属性名(包含Symbol值)
    let keys = Reflect.ownKeys(val);(可换为:Object.keys(val).concat(Object.ownPropertySymbols(val)))
    let len = keys.length;
    while(len--){
        clone[keys[len]] = deepClone(val[keys[len]],map);
    }
    return clone;
}

欢迎交流

307590317 avatar Oct 22 '19 06:10 307590317

const deepCopy = (data, map = new Map()) => {
  let result
  if (map.has(data)) {
    return map.get(data)
  } else if (data instanceof Array) {
    result = []
    map.set(data, result)
    data.forEach(item => result.push(deepCopy(item, map)))
  } else if (typeof data === 'object') {
    result = {}
    map.set(data, result)
    for (let k of Reflect.ownKeys(data)) {
      result[k] = deepCopy(data[k], map)
    }
  } else {
    result = data
  }
  return result
}

maginapp avatar Nov 25 '19 06:11 maginapp

记录一下简单做的

let objc = {
    a: 1,
    b: Symbol('2'),
    c:{
        d: Symbol('3'),
        e: {
            f: Symbol('4'),
        }
    }
};

function deepClone(obj) { 
    let result = {}
    for (const key in obj) {
        typeof (obj[key]) == 'object' ? 
            result[key] = deepClone(obj[key]) : 
            result[key] = obj[key]
    }
    return result
}
let objct = deepClone(objc)
objct.c.e.f = 2
console.log(objc);
console.log(objct);

SSSSSFFFFF avatar Jan 17 '20 07:01 SSSSSFFFFF

function type(data) {
  let type = Object.prototype.toString.call(data)
  if (type === '[object Array]') {
    return []
  } else if (type === '[object Object]') {
    return {}
  } else {
    return data
  }
}

function deepClone(data) {
  let map = new Map() // 处理环状
  let deepCloneFunc = function (data) {
    let result = type(data)
    if (map.get(data)) {  //处理环状
      result = data
      return result
    } 
    if (result !== data) { // 不是基本数据类型 
      map.set(data, result) // 为了判断该对象是否出现过,处理环状
      const objectSymbolsKey = Object.getOwnPropertySymbols(data) // 普通遍历key是获取不到key 为Symbol的
      if (objectSymbolsKey.length) {
        for (let i in objectSymbolsKey) {
          result[objectSymbolsKey[i]] = deepCloneFunc(data[objectSymbolsKey[i]]) 
        }
      }
      for (let key in data) {
        result[key] = deepCloneFunc(data[key])
      }
      return result
    } else {
      return data
    }
  }
  return deepCloneFunc(data)
}
let objx ={}
objx.repeat = objx

let obj = {
  [Symbol('name')]: 'litokele',
  gender: Symbol('male'),
  age: 18,
  favoriteAnime: ['xxx1', 'xxx2'],
  obj: {
    [Symbol('test')]: 'test',
    name: 'kele',
    age: 18
  },
  repeat: objx
}
let myObj = deepClone(obj)

console.log("my_ojb:", myObj)

QQ截图20200330152443

litokele2018 avatar Mar 30 '20 07:03 litokele2018

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

    var instance = new target.constructor
    hash.set(target, instance)
    
    Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)).forEach(key => {
        instance[key] = deepClone(target[key], hash)
    })

    return instance
}

nbili avatar Mar 31 '20 04:03 nbili

function cloneObj(obj) { if (obj === null) return null; if (typeof obj !== 'object') return obj; if (obj.constructor === Date) return new Date(obj); if (obj.constructor === RegExp) return new RegExp(obj); var newObj = new obj.constructor(); for (var key in obj) { if (obj.hasOwnProperty(key)) { var val = obj[key]; newObj[key] = typeof val === 'object' ? cloneObj(val) : val; } } return newObj; };

1368725603 avatar Apr 16 '20 04:04 1368725603

流下了没有技术的眼泪...

coveyz avatar May 06 '20 08:05 coveyz

少了个find函数

const find = (cache, fn) => cache.filter(fn)[0]

分享一个来自 Vuex 的 deepCopy 解决了循环引用,cache 存储所有嵌套 obj 及其 copy 副本,以 cache 中是否有某个嵌套 obj 来判断是否循环引用,有则返回 引用的 copy

export function deepCopy (obj, cache = []) {
  // just return if obj is immutable value
  if (obj === null || typeof obj !== 'object') {
    return obj
  }

  // if obj is hit, it is in circular structure
  const hit = find(cache, c => c.original === obj)
  if (hit) {
    return hit.copy
  }

  const copy = Array.isArray(obj) ? [] : {}
  // put the copy into cache at first
  // because we want to refer it in recursive deepCopy
  cache.push({
    original: obj,
    copy
  })

  Object.keys(obj).forEach(key => {
    copy[key] = deepCopy(obj[key], cache)
  })

  return copy
}

korylee avatar May 06 '20 13:05 korylee

// 咱来个简单易懂的
function deepClone(obj) {
	let newObj = Array.isArray(obj) ? [...obj] : {...obj}
	Reflect.ownKeys(obj).forEach(o => {
		if(obj[o] && typeof obj[o] === 'object') {
			newObj[o] = deepClone(obj[o])
		} else {
			newObj[o] = obj[o]
		}
	})
	return newObj
}

shadow-Fiend avatar Jun 17 '20 07:06 shadow-Fiend

参考上面几位大佬的答案整理了一下

function deepClone(target, cache = []) {
  if (typeof target !== 'object') {
    return target
  }

  let hit = cache.filter(item => item.origin === target)[0];
  if (hit) {
    return hit.copy;
  }

  if (Array.isArray(target)) {
    return target.map(item => {
      return deepClone(item, cache);
    })
  }
  let copy = target.constructor === Object ? {} : Object.create(target.constructor.prototype)
  cache.push({
    origin: target,
    copy
  })
  return [...Object.keys(target), ...Object.getOwnPropertySymbols(target)].reduce((clone, key) => {
    clone[key] = deepClone(target[key], cache);
    return clone;
  }, copy)
}

yuanxiang1990 avatar Jul 10 '20 09:07 yuanxiang1990

function deepClone(obj, hash = new WeakMap()) {
    if (hash.has(obj)) return obj;
    var cobj;
    // null
    if (obj === null) { return obj }
    let t = typeof obj;

    // 基本类型
    switch (t) {
        case 'string':
        case 'number':
        case 'boolean':
        case 'undefined':
            return obj;
    }

    // 数组
    if (Array.isArray(obj)) {
        cobj = [];
        obj.forEach((c, i) => { cobj.push(deepClone(obj[i])) });
    } else {
        cobj = {};
        // object // symbol
        if (Object.prototype.toString.call(obj) === "[object Object]") {
            Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)).forEach(c => {
                hash.set(obj, obj);
                cobj[c] = deepClone(obj[c], hash);
            });
        } else {
            //内置Object
            cobj = obj;
        }
    }
    return cobj;
}

image

不知道有没有漏的。。 你处理循环引用错了。

Bigzo avatar Sep 07 '20 09:09 Bigzo

function nativeDeepCopy (origin, hash = new WeakMap()) {
    const type = typeof origin
    let clone
    if (origin === null || type !== 'function' && type !== 'object') return origin
    if (hash.has(origin)) return hash.get(origin)
    try {
        clone = new origin.constructor()
    } catch(e) {
        clone = Object.create(Object.getPrototypeOf(origin))
    }
    hash.set(origin, clone)
    let keys = [...Object.getOwnPropertyNames(origin), ...Object.getOwnPropertySymbols(origin)]
    for (let i = 0; i < keys.length; i++) {
        let descriptor = Object.getOwnPropertyDescriptor(origin, keys[i])
        descriptor.value = nativeDeepCopy(descriptor.value, hash)
        Object.defineProperty(clone, keys[i], descriptor)
    }
    return clone
}

muzichen avatar Sep 08 '20 06:09 muzichen

记录一下

const init = (origin) => {
  let type = Object.prototype.toString.call(origin)
  if (type === '[object Array]') {
    return []
  } else if (type === '[object Object]') {
    return {}
  }
}

const deepCopy = (origin, map = new WeakMap()) => {
  const type = typeof origin

  if(!type || (type !== 'function' && type !== 'object')) return origin
  if(map.has(origin)) return map.get(origin) // 相互引用

  if(type == 'function') return origin.bind(null)

  let clone = init(origin)
  map.set(origin, clone)
  
  const opk = Object.getOwnPropertyNames(origin)
  const osk = Object.getOwnPropertySymbols(origin) // Symbol
  const keys = [...opk, ...osk]
  for(let i of keys){
    clone[i] = deepCopy(origin[i], map)
  }

  return clone
}

let circle = {}
circle.repeat = circle

let obj = {
  [Symbol('name')]: '7yue',
  age: 26,
  gender: Symbol('male'),
  favor: ['fav1', 'fav2'],
  say: function(){},
  friends: {
    p1: { [Symbol('name')]: 'p1', age: 24, gender: Symbol('male')},
    p2: { [Symbol('name')]: 'p1', age: 27, gender: Symbol('male')}
  },
  repeat: circle
}

let c = deepCopy(obj)

m7yue avatar Sep 10 '20 09:09 m7yue