JS怎么做才算是深拷贝

JavaScript024

JS怎么做才算是深拷贝,第1张

type函数

首先我们要实现一个getType函数对元素进行类型判断,关于元素的类型判断,可以参考我另一篇博文 js中typeof和instanceof详解 ,这里用一个更简便的方法,直接调用Object.prototype.toString 方法。

function getType(obj){

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

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)]

}1234567891011121314151617181920

深拷贝(deepClone)

对于一个引用类型,如果直接将它赋值给另一个变量,由于这两个引用指向同一个地址,这时改变其中任何一个引用,另一个都会受到影响。当我们想复制一个对象并且切断与这个对象的联系,就要使用深拷贝。对于一个对象来说,由于可能有多层结构,所以我们可以使用递归来解决这个问题

function deepClone(data){

var type = getType(data) var obj if(type === 'array'){

obj = []

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

obj = {}

} else { //不再具有下一层次

return data

} if(type === 'array'){ for(var i = 0, len = data.lengthi <leni++){

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

}

} else if(type === 'object'){ for(var key in data){

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

}

} return obj

}12345678910111213141516171819202122

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

广度优先遍历

上面是使用递归来进行深拷贝,显然我们可以使用树的广度优先遍历来实现

//这里为了阅读方便,只深拷贝对象,关于数组的判断参照上面的例子

function deepClone(data){

var obj = {} var originQueue = [data] var copyQueue = [obj] //以下两个队列用来保存复制过程中访问过的对象,以此来避免对象环的问题(对象的某个属性值是对象本身)

var visitQueue = [] var copyVisitQueue = [] while(originQueue.length >0){ var _data = originQueue.shift() var _obj = copyQueue.shift()

visitQueue.push(_data)

copyVisitQueue.push(_obj) for(var key in _data){ var _value = _data[key] if(typeof _value !== 'object'){

_obj[key] = _value

} else { //使用indexOf可以发现数组中是否存在相同的对象(实现indexOf的难点就在于对象比较)

var index = visitQueue.indexOf(_value) if(index >= 0){

_obj[key] = copyVisitQueue[index]

}

originQueue.push(_value)

_obj[key] = {}

copyQueue.push(_obj[key])

}

}

} return obj

}12345678910111213141516171819202122232425262728293031

JSON

深拷贝对象还有另一个解决方法,在对象中不含有函数的时候,使用JSON解析反解析就可以得到一个深拷贝对象

阅读全文

我们先来复习下数据类型相关知识:

此处引申知识点:基本数据类型

这里所说的赋值是对象的引用赋值,当我们把一个对象赋值给一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

由此可见浅拷贝只解决了第一层的问题,如果接下去的值中还有对象,两者享有相同的地址。

深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。