美文网首页
1-js对象/数组的深拷贝和浅拷贝

1-js对象/数组的深拷贝和浅拷贝

作者: 少年啊兵 | 来源:发表于2018-10-24 15:30 被阅读0次

JavaScript有两种数据类型

基础数据类型(Undefined、Null、Boolean、Number 和 String). 变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。

引用数据类型(Array、Object),变量保存的是一个指针。当需要访问引用类型时,先获得地址指针,在从内存中取得数据。

JavaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。


浅复制:

浅复制是复制引用,复制后的引用都是指向同一个对象的实例,彼此之间的操作会互相影响。(对于直接赋值是不会影响的)

如图:

1基础数据类型的浅复制

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

对于深度克隆,我们需要使用其他替代方法,因为Object.assign()复制属性值。如果源值是对象的引用,则它仅复制该引用值。  

2 object.assign()

深复制:

Array的slice和concat方法

不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。之所以把它放在深拷贝里,是因为它看起来像是深拷贝。而实际上它是浅拷贝。 类似于Object.assign()

JSON对象的parse和stringify

JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝。

3、JSON.parse / JSON.stringify 

但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。

4、jQuery.extend()方法源码实现

jQuery的源码 - src/core.js #L121源码及分析如下:

jQuery.extend = jQuery.fn.extend = function() { //给jQuery对象和jQuery原型对象都添加了extend扩展方法

  var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},

  i = 1,

  length = arguments.length,

  deep = false;

  //以上其中的变量:options是一个缓存变量,用来缓存arguments[i],name是用来接收将要被扩展对象的key,src改变之前target对象上每个key对应的value。

  //copy传入对象上每个key对应的value,copyIsArray判定copy是否为一个数组,clone深拷贝中用来临时存对象或数组的src。

  // 处理深拷贝的情况

  if (typeof target === "boolean") {

    deep = target;

    target = arguments[1] || {};

    //跳过布尔值和目标

    i++;

  }

  // 控制当target不是object或者function的情况

  if (typeof target !== "object" && !jQuery.isFunction(target)) {

    target = {};

  }

  // 当参数列表长度等于i的时候,扩展jQuery对象自身。

  if (length === i) {

    target = this; --i;

  }

  for (; i < length; i++) {

    if ((options = arguments[i]) != null) {

      // 扩展基础对象

      for (name in options) {

        src = target[name];

        copy = options[name];

        // 防止永无止境的循环,这里举个例子,

            // 如 var a = {name : b};

            // var b = {name : a}

            // var c = $.extend(a, b);

            // console.log(c);

            // 如果没有这个判断变成可以无限展开的对象

            // 加上这句判断结果是 {name: undefined}

        if (target === copy) {

          continue;

        }

        if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {

          if (copyIsArray) {

            copyIsArray = false;

            clone = src && jQuery.isArray(src) ? src: []; // 如果src存在且是数组的话就让clone副本等于src否则等于空数组。

          } else {

            clone = src && jQuery.isPlainObject(src) ? src: {}; // 如果src存在且是对象的话就让clone副本等于src否则等于空数组。

          }

          // 递归拷贝

          target[name] = jQuery.extend(deep, clone, copy);

        } else if (copy !== undefined) {

          target[name] = copy; // 若原对象存在name属性,则直接覆盖掉;若不存在,则创建新的属性。

        }

      }

    }

  }

  // 返回修改的对象

  return target;

};

jQuery的extend方法使用基本的递归思路实现了浅拷贝和深拷贝,但是这个方法也无法处理源对象内部循环引用,例如:

var a = {"name":"aaa"};

var b = {"name":"bbb"};

a.child = b;

b.parent = a;

$.extend(true,{},a);//直接报了栈溢出。Uncaught RangeError: Maximum call stack size exceeded

5、自己动手实现一个拷贝方法

(function ($) {

    'use strict';

    var types = 'Array Object String Date RegExp Function Boolean Number Null Undefined'.split(' ');

function type () {

  return Object.prototype.toString.call(this).slice(8, -1);

}

for (var i = types.length; i--;) {

    $['is' + types[i]] = (function (self) {

        return function (elem) {

          return type.call(elem) === self;

        };

    })(types[i]);

}

    return $;

})(window.$ || (window.$ = {}));//类型判断

function copy (obj,deep) {

    if ($.isFunction(obj)) {

    return new Function("return " + obj.toString())();

    } else if (obj === null || (typeof obj !== "object")) {

        return obj;

    } else {

        var name, target = $.isArray(obj) ? [] : {}, value;

        for (name in obj) {

            value = obj[name];

            if (value === obj) {

            continue;

            }

            if (deep) {

                if ($.isArray(value) || $.isObject(value)) {

                    target[name] = copy(value,deep);

                } else if ($.isFunction(value)) {

                    target[name] = new Function("return " + value.toString())();

                } else {

                target[name] = value;

                }

            } else {

            target[name] = value;

            }

        }

        return target;

    }        

}

相关文章

  • JS中对象的复制

    JS中的对象复制分为两种情况:深拷贝和浅拷贝。深拷贝和浅拷贝的区别在于对数组和对象的拷贝,对它们拷贝时浅拷贝只是拷...

  • js深拷贝和浅拷贝区别

    浅拷贝和深拷贝的区别 1.浅拷贝:将源对象或者原数组的引用 ```直接赋给``` 新对象/新数组 ,新对象/新...

  • 浅拷贝与深拷贝

    /*浅拷贝:拷贝地址*/ /*深拷贝:拷贝对象*/ 用Strong修饰不可变数组:浅拷贝 用Copy修饰不可变数组...

  • C++封装(二)

    第2章 对象成员与对象数组 第3章 深拷贝与浅拷贝 浅拷贝: 深拷贝: 第4章 对象指针 对象指针: 栈中: 对象...

  • 2018-10-10函数基础

    深拷贝和浅拷贝 深拷贝 copy.deepcopy(对象)浅拷贝 copy.copy(对象)深拷贝: 将对象对应的...

  • java 对象的拷贝

    拷贝:即复制 对象拷贝:即对象复制 java 对象拷贝分类:浅拷贝、深拷贝 java 对象的浅拷贝和深拷贝针对包含...

  • iOS深拷贝(MutableCopy)与浅拷贝(Copy)的区别

    深拷贝和浅拷贝的概念 iOS中有深拷贝和浅拷贝的概念,那么何为深拷贝何为浅拷贝呢?浅拷贝:浅拷贝并不拷贝对象本身,...

  • 1-js对象/数组的深拷贝和浅拷贝

    JavaScript有两种数据类型 基础数据类型(Undefined、Null、Boolean、Number 和 ...

  • 深拷贝和浅拷贝

    1. 深拷贝和浅拷贝 1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用...

  • 关于几个拷贝的问题

    数组浅拷贝 数组深拷贝 复合数组深拷贝

网友评论

      本文标题:1-js对象/数组的深拷贝和浅拷贝

      本文链接:https://www.haomeiwen.com/subject/fdrswftx.html