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

[js] 第44天 深度克隆对象的方法有哪些,并把你认为最好的写出来 #167

Open
haizhilin2013 opened this issue May 29, 2019 · 26 comments
Labels
js JavaScript

Comments

@haizhilin2013
Copy link
Collaborator

第44天 深度克隆对象的方法有哪些,并把你认为最好的写出来

@haizhilin2013 haizhilin2013 added the js JavaScript label May 29, 2019
@AnsonZnl
Copy link
Contributor

AnsonZnl commented May 30, 2019

我比较喜欢使用原生的方法,足够简单,而且可以解决大多数的深拷贝。

var  obj1={
    name: '张三',
    age:14,
    friend:['小红','小白'],
    parents:{
        mother:{name: '李岚',age:34},
        father:{name: '张武',age:35}
    }
}
var obj2 = JSON.parse(JSON.stringify(obj1))
obj1.mother === obj2.parents.mother//false

@wenyejie
Copy link

我比较喜欢使用原生的方法,足够简单,而且可以解决大多数的深拷贝。

var  obj1={
    name: '张三',
    age:14,
    friend:['小红','小白'],
    parents:{
        mother:{name: '李岚',age:34},
        father:{name: '张武',age:35}
    }
}
var obj2 = JSON.parse(JSON.stringify(obj1))
obj1.mother === obj2.parents.mother//false

是挺好的, 就是容易出bug

@rocky-191
Copy link

递归调用拷贝。json.parse可以解决一般的对象深拷贝,但是函数类型的对象就不行了

@tzjoke
Copy link

tzjoke commented May 31, 2019

全面的deep clone最坑爹的一点就是对象内部引用自己

@myprelude
Copy link

  • 容易理解是 JSON.parse JSON.stringify
  • 递归
// 深度遍历

// 广度遍历

@DarthVaderrr
Copy link

数组: newArr=[...oldArr]
只包含数据的对象: JSON.parse
复杂对象:递归,解构

@zlx362211854
Copy link

数组: newArr=[...oldArr]
只包含数据的对象: JSON.parse
复杂对象:递归,解构

对一维数组,才能使用newArr=[...oldArr]

@getanimation
Copy link

数组clone:Array.from( )
对象clone:JSON.parse(JSON.stringify( ))

@chenyouf1996
Copy link

function deepClone(obj) {
  if (Object.prototype.toString.call(obj).slice(8, -1) === 'Object') {
    var newObj = {}
    for (const key in obj) {
      newObj[key]=deepClone(obj[key])
    }
    return newObj
  } else if (Object.prototype.toString.call(obj).slice(8, -1) === 'Array') {
    var newArr = []
    for (const index in obj) {
      newArr[index]=deepClone(obj[index])
    }
    return newArr
  }
  return obj
}

@shufangyi
Copy link

const isType = type => target =>
  Object.prototype.toString.call(target) === `[object ${type}]`

const isArray = isType('Array')
const isObject = isType('Object')

function extend(target, source, deep) {
  for (const key in source)
    if (source.hasOwnProperty(key))
      if (deep && (isArray(source[key]) || isObject(source[key]))) {
        if (isArray(source[key]) && !isArray(target[key])) target[key] = []
        if (isObject(source[key]) && !isObject(target[key])) target[key] = {}
        extend(target[key], source[key], deep)
      } else if (source[key] !== undefined) target[key] = source[key]
}

function clone(target) {
  let deep,
    args = [].slice.call(arguments, 1)
  if (typeof target === 'boolean') {
    deep = target
    target = args.shift()
  }
  args.forEach(source => extend(target, source, deep))
  return target
}

@hc951221
Copy link

hc951221 commented Aug 8, 2019

我习惯用json.parser(json.stringify()),
麻烦点的是自己写个方法把对象遍历出来赋值

@jiamianmao
Copy link

jiamianmao commented Aug 10, 2019

数组: newArr=[...oldArr]
只包含数据的对象: JSON.parse
复杂对象:递归,解构

这个小老弟, 扩展运算符那个是浅拷贝,深拷贝最简单的是 JSON.parse JSON.stringify

但对于 undefined function symbol 是会忽略的。

你API层面得到的都是浅拷贝。

通常来说,都是递归来实现一个 deepClone

@ducky-YFH
Copy link

function deepCopy(newObj, oldObj) {
  for (var key in oldObj) {
    var item = oldObj[key]
    if (item instanceof Array) {
      newObj[key] = [];
      deepCopy(newObj[key], item);
    }
    else if (item instanceof Object) {
      newObj[key] = {};
      deepCopy(newObj[key], item);
    }
    else {
      newObj[key] = item;
    }
  }
  return newObj;
}

@geng130127
Copy link

geng130127 commented Nov 20, 2019

function deepCopy(source, target) {
  const c = target || {}
  for (const item in source) {
    if (typeof source[item] === 'object') {
      c[item] = (source[item].constructor === Array) ? [] : {}
      deepCopy(source[item], c[item])
    } else {
      c[item] = source[item]
    }
  }
  return c
}

@xuwencheng
Copy link

function deepclone(t) {
    let _t = t instanceof Array ? [] : {}
    for (const key in t) {
        _t[key] = typeof t[key] === 'object' ? deepclone(t[key]) : t[key]
    }
    return _t
}

@zlqGitHub
Copy link

zlqGitHub commented Feb 4, 2020

递归实现

function cloneDeep(source) {
    var temp = source.constructor == Array ? [] : {};
    for (var keys in source) {
        if (source.hasOwnProperty(keys)) {
            if (source[keys] && typeof source[keys] === 'object') {
                temp[keys] = source[keys].construct === Array ? [] : {};
                temp[keys] = cloneDeep(source[keys]);
            } else {
                temp[keys] = source[keys]
            }
        }
    }
    return temp;
}

@Ziyu-Chen
Copy link

Ziyu-Chen commented Feb 29, 2020

const deepCopy = obj => { if (typeof obj === "object") { let newObj; if (Array.isArray(obj)) newObj = []; else newObj = {}; for (const key in obj) { newObj[key] = deepCopy(obj[key]); } return newObj; } return obj; };

@lizhesystem
Copy link

deepClone = (element) => {
   if(!(typeof element === 'object')) return element;
   if(element === null) return null;
    return element instanceof Array 
       ? element.map(item => deepClone(item))
       : Object.entries(element)
       .reduce((pre,[key,val])=> ({...pre, [key]: deepClone(val)}), {});
}

// 
const test1 = [1,[2],[[3]],{a:1},{b:{c:1}}];
const test2 = {a: [1], b: {c: 2}};
console.log(test1, deepClone(test1));
console.log(test2, deepClone(test2));

@blueRoach
Copy link

      function deepClone(obj){
        if(typeof obj !== 'object'){
          return new Error('Error arguments')
        }
        let target = Array.isArray(obj) ? [] : {}
        for(let key in obj){
          if(obj && obj.hasOwnProperty(key)){
            if(typeof obj[key] === 'object'){
              target[key] = deepClone(obj[key])
            }else{
              target[key] = obj[key]
            }
          }
        }
        return target
      }

@276378532
Copy link

简单的拷贝

递归拷贝  ==>解决了循环引用
        function _clone(target, map = new Map()) {
            if (typeof target !== 'object') {
                return target;
            }
            let clonetarget = target instanceof Array ? [] : {}
            if (map.has(target)) {
                return map.get(target);
            }
            map.set(target, clonetarget);
            for (const key in target) {
                if (Object.prototype.hasOwnProperty.call(target, key)) {
                    clonetarget[key] = typeof target[key] === 'object' ? _clone(target[key], map) : target[key];
                }
            }
            return clonetarget;
        }

完整点的

const mapTag = '[object Map]';
        const setTag = '[object Set]';
        const arrayTag = '[object Array]';
        const objectTag = '[object Object]';
        const argsTag = '[object Arguments]';

        const boolTag = '[object Boolean]';
        const dateTag = '[object Date]';
        const numberTag = '[object Number]';
        const stringTag = '[object String]';
        const symbolTag = '[object Symbol]';
        const errorTag = '[object Error]';
        const regexpTag = '[object RegExp]';
        const funcTag = '[object Function]';

        const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];


        function forEach(array, iteratee) {
            let index = -1;
            const length = array.length;
            while (++index < length) {
                iteratee(array[index], index);
            }
            return array;
        }

        function isObject(target) {
            const type = typeof target;
            return target !== null && (type === 'object' || type === 'function');
        }

        function getType(target) {
            return Object.prototype.toString.call(target);
        }

        function getInit(target) {
            const Ctor = target.constructor;
            return new Ctor();
        }

        function cloneSymbol(targe) {
            return Object(Symbol.prototype.valueOf.call(targe));
        }

        function cloneReg(targe) {
            const reFlags = /\w*$/;
            const result = new targe.constructor(targe.source, reFlags.exec(targe));
            result.lastIndex = targe.lastIndex;
            return result;
        }

        function cloneFunction(func) {
            const bodyReg = /(?<={)(.|\n)+(?=})/m;
            const paramReg = /(?<=\().+(?=\)\s+{)/;
            const funcString = func.toString();
            if (func.prototype) {
                const param = paramReg.exec(funcString);
                const body = bodyReg.exec(funcString);
                if (body) {
                    if (param) {
                        const paramArr = param[0].split(',');
                        return new Function(...paramArr, body[0]);
                    } else {
                        return new Function(body[0]);
                    }
                } else {
                    return null;
                }
            } else {
                return eval(funcString);
            }
        }

        function cloneOtherType(targe, type) {
            const Ctor = targe.constructor;
            switch (type) {
                case boolTag:
                case numberTag:
                case stringTag:
                case errorTag:
                case dateTag:
                    return new Ctor(targe);
                case regexpTag:
                    return cloneReg(targe);
                case symbolTag:
                    return cloneSymbol(targe);
                case funcTag:
                    return cloneFunction(targe);
                default:
                    return null;
            }
        }

        function clone(target, map = new WeakMap()) {

            // 克隆原始类型
            if (!isObject(target)) {
                return target;
            }

            // 初始化
            const type = getType(target);
            let cloneTarget;
            if (deepTag.includes(type)) {
                cloneTarget = getInit(target, type);
            } else {
                return cloneOtherType(target, type);
            }

            // 防止循环引用
            if (map.get(target)) {
                return target;
            }
            map.set(target, cloneTarget);

            // 克隆set
            if (type === setTag) {
                target.forEach(value => {
                    cloneTarget.add(clone(value));
                });
                return cloneTarget;
            }

            // 克隆map
            if (type === mapTag) {
                target.forEach((value, key) => {
                    cloneTarget.set(key, clone(value));
                });
                return cloneTarget;
            }

            // 克隆对象和数组
            const keys = type === arrayTag ? undefined : Object.keys(target);
            forEach(keys || target, (value, key) => {
                if (keys) {
                    key = value;
                }
                cloneTarget[key] = clone(target[key], map);
            });

            return cloneTarget;
        }

@laboonly
Copy link


function deepCopy (obj) {
    var result;

    //引用类型分数组和对象分别递归
    if (Object.prototype.toString.call(obj) == '[object Array]') {
      result = []
      for (i = 0; i < obj.length; i++) {
        result[i] = deepCopy(obj[i])
      }
    } else if (Object.prototype.toString.call(obj) == '[object Object]') {
      result = {}
      for (var attr in obj) {
        result[attr] = deepCopy(obj[attr])
      }
    }
    //值类型直接返回
    else {
      return obj
    }
    return result
}

@smile-2008
Copy link

我比较喜欢使用原生的方法,足够简单,而且可以解决大多数的深拷贝。

var  obj1={
    name: '张三',
    age:14,
    friend:['小红','小白'],
    parents:{
        mother:{name: '李岚',age:34},
        father:{name: '张武',age:35}
    }
}
var obj2 = JSON.parse(JSON.stringify(obj1))
obj1.mother === obj2.parents.mother//false

@xanthuz
Copy link

xanthuz commented May 16, 2021

@shufangyi

const isType = type => target =>
  Object.prototype.toString.call(target) === `[object ${type}]`

const isArray = isType('Array')
const isObject = isType('Object')

function extend(target, source, deep) {
  for (const key in source)
    if (source.hasOwnProperty(key))
      if (deep && (isArray(source[key]) || isObject(source[key]))) {
        if (isArray(source[key]) && !isArray(target[key])) target[key] = []
        if (isObject(source[key]) && !isObject(target[key])) target[key] = {}
        extend(target[key], source[key], deep)
      } else if (source[key] !== undefined) target[key] = source[key]
}

function clone(target) {
  let deep,
    args = [].slice.call(arguments, 1)
  if (typeof target === 'boolean') {
    deep = target
    target = args.shift()
  }
  args.forEach(source => extend(target, source, deep))
  return target
}

学习了。

/** 将目标对象克隆到一个新对象
* @param target {Object.Array} 克隆到的最新对象或数组
* @param source {Object.Array} 被克隆的原始对象或数组。它要与 target 的类型保持一致。
* @param deep {Boolean} 深克隆的标识。true: 深克隆; false(默认): 浅克隆。
* @return void
*/
extend(target, source, deep)

@HNHED
Copy link

HNHED commented Sep 4, 2021

`<script>
//数组拷贝
//数组浅拷贝
//slice方法、concat方法、Array.from()
//扩展运算符比较特殊、对于一维数组可以实现深拷贝、对于一层对象同样如此
//但是对于多维数组和多层对象来说只能实现浅拷贝
//深拷贝方法
//1.递归

function deepclone(obj) {
    if (Object.prototype.toString.call(obj).slice(8, -1) === 'Object') {
        const newObj = {};
        for (const key in obj) {
            newObj[key] = deepclone(obj[key])
        }
        return newObj;
    } else if (Object.prototype.toString.call(obj).slice(8, -1) === 'Array') {
        const newArr = [];
        for (const index in obj) {
            newArr[index] = deepclone(obj[index]);
        }
        return newArr;
    }
    return obj;
}
let arr = [1, 2, 3, [1, 2, 3], 5];
let obj = {
    name: 'zhangsan',
    course: ['JS', 'CSS', 'HTML']
}
let newarr = deepclone(arr);
arr[3][1] = 9;
console.log(newarr);
let newobj = deepclone(obj);
obj.course[1] = 'Vue';
console.log(newobj);

//2.JSON.parse(JSON.stringify())
//弊端:undefined、function、symbol会在转换过程中被忽略
//3.第三方插件:比如lodash的深拷贝

</script>`

@xiaoqiangz
Copy link

function deepClone(target){
let result
if (res.isArray(target) === 'Array') {
result = []
for(let o in target) {
result.push(deepClone(target[o]))
}
}else if(res.isObject(target) === 'Object') {
result = {}
for(let o in target) {
result[o] = deepClone(target[o])
}
} else { // 基本数据类型
result = target
}
return result
}

最好的感觉还是Json.stringify(obj)

@wyy-g
Copy link

wyy-g commented Sep 19, 2022

function deepClone(obj){
    if(!obj instanceof Object ) return;
    let target = Array.isArray(obj) ? [] : {};
    for(let key in obj){
        if(obj.hasOwnPrototype(key)){
            if(type obj[key] === 'object'){
                target[key] = deepClone(obj[key]);
            }else{
                 target[key] = obj[key];
            }
        }
    }

    return target;
}

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

No branches or pull requests