fe-interview icon indicating copy to clipboard operation
fe-interview copied to clipboard

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

Open haizhilin2013 opened this issue 5 years ago • 26 comments

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

haizhilin2013 avatar May 29 '19 20:05 haizhilin2013

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

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

AnsonZnl avatar May 30 '19 08:05 AnsonZnl

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

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

wenyejie avatar May 30 '19 11:05 wenyejie

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

rocky-191 avatar May 30 '19 14:05 rocky-191

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

tzjoke avatar May 31 '19 01:05 tzjoke

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

// 广度遍历

myprelude avatar Jun 13 '19 06:06 myprelude

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

DarthVaderrr avatar Jul 04 '19 05:07 DarthVaderrr

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

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

zlx362211854 avatar Jul 15 '19 06:07 zlx362211854

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

getanimation avatar Jul 17 '19 07:07 getanimation

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
}

chenyouf1996 avatar Jul 22 '19 15:07 chenyouf1996

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
}

shufangyi avatar Jul 23 '19 06:07 shufangyi

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

hc951221 avatar Aug 08 '19 09:08 hc951221

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

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

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

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

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

jiamianmao avatar Aug 10 '19 10:08 jiamianmao

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

ducky-YFH avatar Oct 22 '19 08:10 ducky-YFH

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
}

geng130127 avatar Nov 20 '19 10:11 geng130127

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
}

xuwencheng avatar Jan 02 '20 09:01 xuwencheng

递归实现

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

zlqGitHub avatar Feb 04 '20 12:02 zlqGitHub

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

Ziyu-Chen avatar Feb 29 '20 12:02 Ziyu-Chen

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

lizhesystem avatar Apr 29 '20 09:04 lizhesystem

      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
      }

blueRoach avatar Jul 21 '20 01:07 blueRoach

简单的拷贝

递归拷贝  ==>解决了循环引用
        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;
        }

276378532 avatar Aug 21 '20 08:08 276378532


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
}

laboonly avatar Aug 31 '20 15:08 laboonly

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

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

MrZ2019 avatar Nov 05 '20 05:11 MrZ2019

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

xanthuz avatar May 16 '21 15:05 xanthuz

`

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的深拷贝

`

HNHED avatar Sep 04 '21 14:09 HNHED

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)

xiaoqiangz avatar Jun 06 '22 05:06 xiaoqiangz

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

wyy-g avatar Sep 19 '22 14:09 wyy-g