1. function clone(obj, cloned = new WeakMap()) {
    2. if (cloned.has(obj)) return cloned.get(obj)
    3. if (obj == null || (typeof obj !== 'object' && typeof obj !== 'function')) {
    4. return obj
    5. }
    6. let newObj = null
    7. // 时间对象,正则
    8. if (obj.constructor === Date || obj.constructor === RegExp) {
    9. newObj = new obj.constructor(obj)
    10. } else if (typeof obj === 'function') {
    11. newObj = eval('(' + obj.toString() + ')')
    12. } else if (obj.clone) {
    13. // 兼容Moments等库
    14. return obj.clone()
    15. } else { //数组,普通对象
    16. newObj = new obj.constructor()
    17. cloned.set(obj, newObj) // 标记改对象已经复制过
    18. for (let key in Object.getOwnPropertyDescriptors(obj)) {
    19. newObj[key] = clone(obj[key], cloned)
    20. }
    21. }
    22. return newObj
    23. }
    24. // 1、函数,日期,正则
    25. let obj = {
    26. a: 1,
    27. fn: function() {console.log('test')},
    28. date: new Date(),
    29. reg: /\d/
    30. }
    31. let o = clone(obj)
    32. console.log(o)
    33. console.log(obj)
    34. console.log(o.fn === obj.fn)
    35. console.log(o.date === obj.date)
    36. console.log(o.reg === obj.reg)
    37. // 2、循环引用
    38. let a = {}
    39. a.b = a
    40. console.log(clone(a))


    const isComplexDataType = obj => (typeof obj === ‘object’ || typeof obj === ‘function’) && (obj !== null)
    const deepClone = function (obj, hash = new WeakMap()) {
    if (obj.constructor === Date)
    return new Date(obj) // 日期对象直接返回一个新的日期对象
    if (obj.constructor === RegExp)
    return new RegExp(obj) //正则对象直接返回一个新的正则对象
    //如果循环引用了就用 weakMap 来解决
    if (hash.has(obj)) return hash.get(obj)
    let allDesc = Object.getOwnPropertyDescriptors(obj)
    //遍历传入参数所有键的特性
    let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
    //继承原型链
    hash.set(obj, cloneObj)
    for (let key of Reflect.ownKeys(obj)) {
    cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== ‘function’) ? deepClone(obj[key], hash) : obj[key]
    }
    return cloneObj
    }
    // 下面是验证代码
    let obj = {
    num: 0,
    str: ‘’,
    boolean: true,
    unf: undefined,
    nul: null,
    obj: { name: ‘我是一个对象’, id: 1 },
    arr: [0, 1, 2],
    func: function () { console.log(‘我是一个函数’) },
    date: new Date(0),
    reg: new RegExp(‘/我是一个正则/ig’),
    [Symbol(‘1’)]: 1,
    };
    Object.defineProperty(obj, ‘innumerable’, {
    enumerable: false, value: ‘不可枚举属性’ }
    );
    obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))
    obj.loop = obj // 设置loop成循环引用的属性
    let cloneObj = deepClone(obj)
    cloneObj.arr.push(4)
    console.log(‘obj’, obj)
    console.log(‘cloneObj’, cloneObj)

    vuex中的深拷贝解决循环引用的问题:(没有考虑函数)

    1. function find (list, f) {
    2. return list.filter(f)[0]
    3. }
    4. /**
    5. * Deep copy the given object considering circular structure.
    6. * This function caches all nested objects and its copies.
    7. * If it detects circular structure, use cached copy to avoid infinite loop.
    8. *
    9. * @param {*} obj
    10. * @param {Array<Object>} cache
    11. * @return {*}
    12. */
    13. function deepCopy (obj, cache = []) {
    14. // just return if obj is immutable value
    15. if (obj === null || typeof obj !== 'object') {
    16. return obj
    17. }
    18. // if obj is hit, it is in circular structure
    19. const hit = find(cache, c => c.original === obj)
    20. if (hit) {
    21. return hit.copy
    22. }
    23. const copy = Array.isArray(obj) ? [] : {}
    24. // put the copy into cache at first
    25. // because we want to refer it in recursive deepCopy
    26. cache.push({
    27. original: obj,
    28. copy
    29. })
    30. Object.keys(obj).forEach(key => {
    31. copy[key] = deepCopy(obj[key], cache)
    32. })
    33. return copy
    34. }

    以上都是通过递归来实现的,我们都知道递归有个缺点,函数每次调用都会形成调用记录,容易爆栈。是不是所有的递归都能用while循环解决呢???

    把一个对象看成一棵树:

    1. var a = {
    2. a1: 1,
    3. a2: {
    4. b1: 1,
    5. b2: {
    6. c1: 1
    7. }
    8. }
    9. }
    1. a
    2. / \
    3. a1 a2
    4. | / \
    5. 1 b1 b2
    6. | |
    7. 1 c1
    8. |
    9. 1

    思路:用循环遍历一棵树,需要借助一个栈,当栈为空时就遍历完了,栈里面存储下一个需要拷贝的节点

    首先我们往栈里放入种子数据,key用来存储放哪一个父元素的那一个子元素拷贝对象

    然后遍历当前节点下的子元素,如果是对象就放到栈里,否则直接拷贝

    1. function cloneLoop(x) {
    2. const root = {};
    3. // 栈
    4. const loopList = [
    5. {
    6. parent: root,
    7. key: undefined,
    8. data: x,
    9. }
    10. ];
    11. while(loopList.length) {
    12. // 深度优先
    13. const node = loopList.pop();
    14. const parent = node.parent;
    15. const key = node.key;
    16. const data = node.data;
    17. // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
    18. let res = parent;
    19. if (typeof key !== 'undefined') {
    20. res = parent[key] = {};
    21. }
    22. for(let k in data) {
    23. if (data.hasOwnProperty(k)) {
    24. if (typeof data[k] === 'object') {
    25. // 下一次循环
    26. loopList.push({
    27. parent: res,
    28. key: k,
    29. data: data[k],
    30. });
    31. } else {
    32. res[k] = data[k];
    33. }
    34. }
    35. }
    36. }
    37. return root;
    38. }

    解决循环引用:

    1. // 保持引用关系
    2. function cloneForce(x) {
    3. // =============
    4. const uniqueList = []; // 用来去重
    5. // =============
    6. let root = {};
    7. // 循环数组
    8. const loopList = [
    9. {
    10. parent: root,
    11. key: undefined,
    12. data: x,
    13. }
    14. ];
    15. while(loopList.length) {
    16. // 深度优先
    17. const node = loopList.pop();
    18. const parent = node.parent;
    19. const key = node.key;
    20. const data = node.data;
    21. // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
    22. let res = parent;
    23. if (typeof key !== 'undefined') {
    24. res = parent[key] = {};
    25. }
    26. // =============
    27. // 数据已经存在
    28. let uniqueData = find(uniqueList, data);
    29. if (uniqueData) {
    30. parent[key] = uniqueData.target;
    31. continue; // 中断本次循环
    32. }
    33. // 数据不存在
    34. // 保存源数据,在拷贝数据中对应的引用
    35. uniqueList.push({
    36. source: data,
    37. target: res,
    38. });
    39. // =============
    40. for(let k in data) {
    41. if (data.hasOwnProperty(k)) {
    42. if (typeof data[k] === 'object') {
    43. // 下一次循环
    44. loopList.push({
    45. parent: res,
    46. key: k,
    47. data: data[k],
    48. });
    49. } else {
    50. res[k] = data[k];
    51. }
    52. }
    53. }
    54. }
    55. return root;
    56. }
    57. function find(arr, item) {
    58. for(let i = 0; i < arr.length; i++) {
    59. if (arr[i].source === item) {
    60. return arr[i];
    61. }
    62. }
    63. return null;
    64. }

    以上两段代码转载自:
    深拷贝的终极探索(90%的人不知道):https://zhuanlan.zhihu.com/p/46789186

    更全面 深拷贝库:https://github.com/planttheidea/fast-copy