javascript深拷贝(deepClone)

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

深拷贝(deepClone)?

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

为啥要用深拷贝?

在不少状况下,咱们都须要给变量赋值,给内存地址赋予一个值,可是在赋值引用值类型的时候,只是共享一个内存区域,致使赋值的时候,还跟以前的值保持一直性。
看一个具体的例子前端

// 给test赋值了一个对象
var test = {
    a: 'a',
    b: 'b'
};

// 将test赋值给test2
// 此时test和test2是共享了同一块内存对象,这也就是浅拷贝
var test2 = test;

test2.a = 'a2';

test.a === 'a2'// 为true

图解:
java

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

实现

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

判断javascript类型

javascript中有如下基本类型github

类型 描述
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表示了一个对象类型][1]数组

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类型,博主这里是直接赋值的,仍是共享一个内存值。这是由于函数更多的是完成某些功能,有个输入值和返回值,并且对于上层业务而言更多的是完成业务功能,并不须要真正将函数深拷贝。dom

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

其实博主只想到了用new来操做一下,可是function就会执行一遍,不敢想象会有什么执行结果哦!o(╯□╰)o!其它暂时尚未什么好的想法,欢迎你们指导哦!

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

浅拷贝?

对于浅拷贝而言,能够理解为只操做一个共同的内存区域!这里会存在危险!(。﹏。*)

若是直接操做这个共享的数据,不作控制的话,会常常出现数据异常,被其它部分更改。因此应该不要直接操做数据源,给数据源封装一些方法,来对数据来进行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/jqu...

  2. underscore的实现
    详见https://github.com/jashkenas/...

  3. lodash的实现
    详见https://github.com/lodash/lod...

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

var obj = {
    a: 'a',    
    b: function(){console.log('b')}
}

//在JSON.stringify的时候就会把function给过滤了。

JSON.stringify(obj)// "{"a":"a"}"

小结

这里大概总结了一下深拷贝,以及怎么实现一个深拷贝。在不一样的场景下,要根据业务场景,判断是否须要使用深拷贝。
原文连接 http://xiaoqiang730730.github...

参考文献

winter-JavaScript中的类型
http://www.cnblogs.com/winter...

相关文章
相关标签/搜索