最近在回顾之前的笔记时,无意间翻阅到了深克隆和浅克隆的模块,感觉其中涉及了很多以后面试中可能会遇到的问题,所以写下此博客用于加深印象!
一、如何实现深克隆与浅克隆?
1、浅克隆
我们可以从其名字中的一个“浅”字展开来说,顾名思义,通过这种方式克隆出来的数据并不能完全脱离原数据,克隆前与克隆后的变量各自的变化却会相互影响,表现的就跟同一个人一样。
原理:
究其原因,这还得追溯回原始值与引用值。引用值是指一些复合类型数据的值,包括Object
,function
,Array
,RegExp
,Data
,引用值于原始值不同,引用值把引用变量存储在栈中,而实际的对象存储在堆中。每一个引用变量都有一根指针指向其堆中的实际对象。
下面来看一个浅克隆的简单例子:
var a = [1,2,3] ;
var b = a ;
a.push(4) ;
console.log(b) ; //输出1,2,3,4
a = [12] ;
console.log(b) ; //输出1,2,3,4
console.log(a) ; //输出12
如下图所示,引用值a赋值给b时,各自的变量名存储在栈中,而实际对象值都指向堆中的同一个地址,当变量a通过方法改变值时,实际上是改变了堆中内容,因此,b的值也会相应的改变。
如下图所示,当a通过非方法改变值时,系统会为该引用变量重新创建一个堆房间,此时,a的指针指向新的堆地址。
原始值与引用值的详细介绍请参考:https://editor.csdn.net/md/%EF%BC%9Ahttps://blog.csdn.net/qq_22896159/article/details/81000178
具体函数:
function clone(origin,target) {
var target = target || {}
for(var k in origin) {
target[k] = origin[k]
}
return target;
}
测试结果:
从测试结果中可以看到,当对a进行了浅克隆后,随即往a的第一个元素(也是一个数组)中添加一个元素,再来查看b的值,发现b的值也随着a的改动而改动了。同理如果来改动b的值,那么a的值也会产生相应的变化,因为它们本质是指向堆中同一块地址的,获取到的数据值自然也相同。
2、深克隆
原理:
Number
,String
,Boolean
,Null
,Undefined
这些基本数据类型都是原始值也即原始类型的值。原始变量的值存储在栈中。原始变量之间的赋值操作本质上就是当一个原始变量把值赋给另一个原始变量时,只是把栈中的内容复制给另一个原始变量,此时这两个变量互不影响,即当一个变量值改变时,另一个变量不会因此而发生任何变化。
下面来看一个深克隆的简单例子:
var a = 10 ;
var b = a ;
a = 20;
console.log(b); //输出10 b的值不会因a的值的改变而该改变
有了这个原理之后,我们就有方法能够将原对象或者数组等等引用变量里的东西完全复制一份。我们只需精细化的处理对象的每一个属性、方法以及数组中的每一个元素,让他们单独进行拷贝到变量中。在这种操作下,引用变量指向的将不再是堆中的同一块地址,因此对于新对象的修改并不会影响到原对象。
具体函数:
* @param {Object} origin 需要克隆的变量信息
* @param {Object} target 装载克隆信息的载体
* @desc 该函数通过递归的思想负责完成一个深克隆变量的操作
*/
function deepClone(origin, target) {
var target = target || {}, // 提高容错率,避免忘记传对应参数而导致函数无用
toStr = Object.prototype.toString, //为了在后续操作中简化代码,我们将该方法引用出来
arrStr = '[object Array]' // 该变量的设置是为了后续判断引用变量是不是数组
for (var prop in origin) {
// 如果我们不想将原变量原型链中的属性拷贝过来,那我们需要在每次循环都进行一次判断
if (origin.hasOwnProperty(prop)) {
// hasOwnProperty () 方法用来检测一个属性是否是对象的自有属性,而不是从原型链继承的。如果该属性是自有属性,那么返回 true,否则返回 false
if (typeof (origin[prop]) === 'object') {
// 代码执行到这里,说明即将拷贝的是引用变量,先根据toString方法判断变量类型,然后再次执行deepClone函数
if (toStr.call(origin[prop]) === arrStr) {
target[prop] = []
} else {
target[prop] = {}
}
deepClone(origin[prop], target[prop])
} else {
// 代码执行到这里,说明即将拷贝的是原始变量,直接赋值就可实现深克隆
target[prop] = origin[prop]
}
}
}
return target; // 为了就算忘记传参数也能将克隆好的变量导出
}
测试结果:
从测试结果中可以看到,对原变量a进行对应元素的改变已经影响不到变量b了,变量b已经完全成为了一个独立的个体,其在今后可以独立使用且不用惧怕影响到变量a。
二、深克隆与浅克隆的区别
浅度克隆:原始类型为值传递,对象类型仍为引用传递。其本质上并没有成为一个单独的个体与原对象脱离。
深度克隆:所有元素或属性均完全复制,与原对象完全脱离,也就是说所有对于新对象的修改都不会反映到原对象中。
以上就是我对深、浅克隆的理解,有理解不到位的地方欢迎大家指正!