关于JSON.parse(JSON.stringify(obj))实现深拷贝应该注意的坑

JSON.parse(JSON.stringify(obj))我们一般用来深拷贝,其过程说白了 就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象;我们在使用 JSON.parse(JSON.stringify(xxx))时应该注意一下几点:

1、如果obj里面有时间对象,

则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;

 var test = {
     
    name: 'a',
    date: [new Date(1536627600000), new Date(1540047600000)],
  };

  let b;
  b = JSON.parse(JSON.stringify(test))


test 结果.
在这里插入图片描述
b的结果
关于JSON.parse(JSON.stringify(obj))实现深拷贝应该注意的坑_第1张图片
测试b和test类型
在这里插入图片描述

2、如果obj里有RegExp、Error对象

,则序列化的结果将只得到空对象;

  const test = {
     
        name: 'a',
        date: new RegExp('\\w+'),
      };
      // debugger
      const copyed = JSON.parse(JSON.stringify(test));
      test.name = 'test'
      console.error('ddd', test, copyed)

测试结果
在这里插入图片描述

3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;

   const test = {
     
        name: 'a',
        date: function hehe() {
     
          console.log('fff')
        },
      };
      // debugger
      const copyed = JSON.parse(JSON.stringify(test));
      test.name = 'test'
      console.error('ddd', test, copyed)

结果
在这里插入图片描述

4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null

 const test = {
     
        name: NaN,
        date: function hehe() {
     
          console.log('fff')
        },
      };
      // debugger
      const copyed = JSON.parse(JSON.stringify(test));
    
      console.log( test, copyed)
    

在这里插入图片描述

5、JSON.stringify()只能序列化对象的可枚举的自有属性

,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;

     function Person(name) {
     
        this.name = name;
        console.log(name)
      }

      const liai = new Person('liai');

      const test = {
     
        name: 'a',
        date: liai,
      };
      // debugger
      const copyed = JSON.parse(JSON.stringify(test));
      test.name = 'test'
      console.error('ddd', test, copyed)

第一个date的构造函数是Person 但是经过序列化再格式化已经失去了原有的构造函数 而是变成了Object
关于JSON.parse(JSON.stringify(obj))实现深拷贝应该注意的坑_第2张图片
接下来这个方法比较通用可以参考

var clone = function (obj) {
      
    if(obj === null) return null 
    if(typeof obj !== 'object') return obj;
    if(obj.constructor===Date) return new Date(obj); 
    var newObj = new obj.constructor ();  //保持继承链
    for (var key in obj) {
     
        if (obj.hasOwnProperty(key)) {
        //不遍历其原型链上的属性
            var val = obj[key];
            newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
        }
    }  
    return newObj;  
}; 

1、用new obj.constructor ()构造函数新建一个空的对象,而不是使用{}或者[],这样可以保持原形链的继承; 这样克隆出来的新对象也可以通过原型链访问构造函数的方法
2、用obj.hasOwnProperty(key)来判断属性是否来自原型链上,因为for…in…也会遍历其原型链上的可枚举属性。
3、上面的函数用到递归算法,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 clone紧紧耦合在了一起。为了消除这种紧密耦合的现象,需要使用 arguments.callee。在严格模式下 arguments.callee 被禁用了,可以查看我的其他文章。

你可能感兴趣的:(js,深拷贝与浅拷贝)