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

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;

    }        

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • /*! jQuery JavaScript Library v1.4.2 http://jquery.com/ C...
    ssttIsme阅读 1,911评论 2 0
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,347评论 1 45
  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些阅读 2,025评论 0 2
  • 过去的,永远回不去。 忘记的,用钱也买不回来。 所以人生经历,就是最宝贵的财富。 不要让自己暮年回望,才发觉什么都...
    茶景阅读 299评论 4 1
  • 今天坐高铁,竟然有15块的套餐,要了一份,吃起来味道还不赖,跟飞机餐很像。以前读书时,舍不得坐卧铺,一路80个小时...
    青三1219阅读 569评论 0 51