js实现深拷贝的几种方法

JavaScript016

js实现深拷贝的几种方法,第1张

简单来说,深拷贝主要是将另一个对象的属性值拷贝过来之后,另一个对象的属性值并不受到影响,因为此时它自己在堆中开辟了自己的内存区域,不受外界干扰。

浅拷贝主要拷贝的是对象的引用值,当改变对象的值,另一个对象的值也会发生变化。

使用 js ON.stringify和 js ON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;

缺陷:它会抛弃对象的 const ructor,深拷贝之后,不管这个对象原来的构造 函数 是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON;

递归拷贝实现深拷贝,解决循环引用问题

在JS中拷贝、复制一个对象的方式有多种,我常用的一般是 Object.assign({},sourceObj)。

Object.assign()因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。如果合并的对象是多层嵌套对象那就属于浅拷贝了,修改内层对象的值还是会影响原对象。

要想实现深度复制可以使用JSON方式。

写法如下:

它的原理是把JS对象转换为JSON字符串,再由JSON字符串转换为JS对象,这样新对象的指针就不会指向原对象的指针了。但这种也有副作用的,有一下几点副作用:

1、有属性包含时间对象,拷贝后就是字符串的形式。

2、有函数或undefined,拷贝后会丢失。

3、有RegExp和Error对象,拷贝后变为空对象。

4、存在循环引用的情况,没法正确拷贝。

5、内层对象是构造函数生成的,会丢失constructor。

6、属性值为NaN、Infinity,拷贝后变为null。

虽然有些副作用,但大多情况还是符合要求可以使用。

function deepClone(data) {

      const type = this.judgeType(data)

      let obj

      if (type === 'array') {

        obj = []

      } else if (type === 'object') {

        obj = {}

      } else {

    // 不再具有下一层次

        return data

      }

      if (type === 'array') {

        // eslint-disable-next-line

        for (let i = 0, len = data.lengthi <leni++) {

          obj.push(this.deepClone(data[i]))

        }

      } else if (type === 'object') {

        // 对原型上的方法也拷贝了....

        // eslint-disable-next-line

        for (const key in data) {

          obj[key] = this.deepClone(data[key])

        }

      }

      return obj

    }

function  judgeType(obj) {

  // tostring会返回对应不同的标签的构造函数

      const toString = Object.prototype.toString

      const map = {

        '[object Boolean]': 'boolean',

        '[object Number]': 'number',

        '[object String]': 'string',

        '[object Function]': 'function',

        '[object Array]': 'array',

        '[object Date]': 'date',

        '[object RegExp]': 'regExp',

        '[object Undefined]': 'undefined',

        '[object Null]': 'null',

        '[object Object]': 'object',

      }

      if (obj instanceof Element) {

        return 'element'

      }

      return map[toString.call(obj)]

    }