首页 网络技术 系统安全

Javascript深拷贝

Javascript深拷贝

javascript深拷贝是初学者甚至有经验的开发者,都会经常遇到问题,并不能很好的理解javascript的深拷贝。

深拷贝(deepClone)?

与深拷贝相对的就是浅拷贝,很多初学者在接触这个感念的时候,是很懵逼的。

为啥要用深拷贝?

在很多情况下,我们都需要给变量赋值,给内存地址赋予一个值,但是在赋值引用值类型的时候,只是共享一个内存区域,导致赋值的时候,还跟之前的值保持一直性。

看一个具体的例子

// 给test赋值了一个对象  var test = {  a: 'a',  b: 'b'  };     // 将test赋值给test2  // 此时test和test2是共享了同一块内存对象,这也就是浅拷贝  var test2 = test;     test2.a = 'a2';     test.a === 'a2'// Ϊtrue  

图解:

这下就很好理解为什么引用值类型数据相互影响问题。

实现

实现一个深拷贝函数,就不得不说javascript的数值类型。

判断javascript类型

javascript中有以下基本类型

类型 描述 undefined undefined类型只有一个值undefined,它是变量未被赋值时的值 null null类型也只有一个值null, 它是一个空的对象引用 Boolean Boolean有两种取值true和false String 它表示文本信息 Number 它表示数字信息 Object 它是一系列属性的无序集合, 包括函数Function和数组Array

使用typeof是无法判断function和array的,这里使用Object.prototype.toString方法。 默认情况下,每个对象都会从Object上继承到toString()方法,如果这个方法没有被这个对象自身或者更接近的上层原型上的同名方法覆盖(遮蔽),则调用该对象的toString()方法时会返回”[object type]”,这里的字符串type表示了一个对象类型

function type(obj) {  var toString = Object.prototype.toString;  var 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'  };  return map[toString.call(obj)];  }  

实现deepClone

对于非引用值类型的数值,直接赋值,而对于引用值类型(object)还需要再次遍历,递归赋值。

function deepClone(data) {  var t = type(data), o, i, ni;  if(t === 'array') {      o = [];  }else if( t === 'object') {      o = {};  }else {      return data;  }  if(t === 'array') {      for (i = 0, ni = data.length; i < ni; i++) {          o.push(deepClone(data[i]));      }      return o;  }else if( t === 'object') {      for( i in data) {          o[i] = deepClone(data[i]);      }      return o;  }  }  

这里有个点大家要注意下,对于function类型,博主这里是直接赋值的,还是共享一个内存值。这是因为函数更多的是完成某些功能,有个输入值和返回值,而且对于上层业务而言更多的是完成业务功能,并不需要真正将函数深拷贝。

但是function类型要怎么拷贝呢?

到这里差不多也就实现完了深拷贝,又有人觉的怎么没有实现浅拷贝呢?

浅拷贝?

如果直接操作这个共享的数据,不做控制的话,会经常出现数据异常,被其它部分更改。所以应该不要直接操作数据源,给数据源封装一些方法,来对数据来进行CURD操作。

到这里估计就差不多了,但是作为一个前端,不仅仅考虑javascript本身,还得考虑到dom、浏览器等。

Element类型

来看下面代码,结果会返回啥呢?

Object.prototype.toString.call(document.getElementsByTagName('div')[0]) 

答案是[object HTMLDivElement]

有时候保存了dom元素, 一不小心进行深拷贝,上面的深拷贝函数就缺少了对Element元素的判断。而判断Element元素要使用instanceof来判断。因为对于不同的标签,tostring会返回对应不同的标签的构造函数。

function type(obj) {  var toString = Object.prototype.toString;  var 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)];  }  

其它方式?

1. jquery的实现

详见https://github.com/jquery/jquery/blob/master/src/core.js

2. underscore的实现

详见https://github.com/jashkenas/underscore/blob/master/underscore.js

3. lodash的实现

详见https://github.com/lodash/lodash/blob/master/lodash.js

4. JSON实现

先通过JSON.stringify一下,然后再JSON.parse一下,就能实现深拷贝。但是数据类型只支持基本数值类型。

var obj = {      a: 'a',          b: function(){console.log('b')}  }     //在JSON.stringify的时候就会把function给过滤了。     JSON.stringify(obj)// "{"a":"a"}"  

小结

这里大概总结了一下深拷贝,以及怎么实现一个深拷贝。在不同的场景下,要根据业务场景,判断是否需要使用深拷贝。

参考文献

winter-JavaScript中的类型 http://www.cnblogs.com/winter-cn/archive/2009/12/07/1618281.html


点赞 0
广告 aliyun

选择养成模拟类手游推荐 打造你的温馨居所

养成模拟类手游推荐 打造你的温馨居所 养成模拟类手游推荐 打造你的温馨居所

春节假期转瞬即逝,大家在这几天的假期中感受到了亲朋好友的问候以及家的温暖,但是我们即将...[详细]

比拼

官方微博/微信

每日头条、业界资讯、热点资讯、八卦爆料,全天跟踪微博播报。各种爆料、内幕、花边、资讯一网打尽。百万互联网粉丝互动参与,TechWeb官方微博期待您的关注。

↑扫描二维码

想在手机上看科技资讯和科技八卦吗?

想第一时间看独家爆料和深度报道吗?

请关注TechWeb官方微信公众帐号:

1.用手机扫左侧二维码;

2.在添加朋友里,搜索关注TechWeb。

为您推荐

加载更多文章