JS随笔:关于原始值和引用值的浅层克隆和深层克隆
在学习的过程中写这篇文章,总结一些学习克隆要点
-
在进行克隆的环节前要了解什么是原始值和引用值
在 ECMAScript 中,变量可以存在两种类型的值,即原始值和引用值。以下是W3C的定义
1.原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
有五个值:Undefined,Null,Boolean,Number,String
2.引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。
包括 Object(Array也是Object)、Function、Date、RegExp。 -
克隆的过程
1.遍历你所要克隆的对象/数组:for(var prop in obj) / for(var prop in arr)
ps:数组是一个特殊的对象,因此for in 循环通吃数组和对象2.判断是不是原始值
typeof() ,返回是object基本是引用值,除了null,其他是原始值3.判断是数组还是对象
识别一个变量里面传的的数组还是对象有三种方法
(1).constructor (2).instanceof (3).toString()方法4.重新建立一个空的,相应的数组或对象
5.递归(递归核心思想:(1)找规律 (2)找出口)
浅层克隆:针对克隆的值是原始值,没有引用值时才能使用的。
因为原始值存储在栈中。意思就是说,当一个原始变量把值赋给另一个原始变量时,只是把栈中的内容复制给另一个原始变量,此时这两个变量互不影响,即当一个变量值改变时,另一个变量不会因此而发生任何变化。
简单来说:就是克隆了另外一个仓库,有两个仓库,互不影响,但这是克隆对象只有原始值的情况下
例子:
var obj = {
name :'wzy',
sex:'male',
age:24
}
var obj1 = {};
function clone(origin,target){
var target = target || {};
for(var prop in origin){
target[prop] = origin[prop]
}
return target;
}
clone(obj,obj1);
这个例子obj的值全部是原始值,因此直接遍历对象然后克隆
深层克隆:无论是原始值还是引用值都互不影响
当克隆的对象包含引用值时,这时候改变克隆的值,被克隆的对象也会改变。
因为引用值于原始值不同,引用值把引用变量存储在栈中,而实际的对象存储在堆中。每一个引用变量都有一根指针指向其堆中的实际对象。当变量改变时,另一个变量也会改变.因此要用深度克隆
简单来说:引用值其实提供一个仓库的地址,你改变了这个仓库的东西,那么你克隆的对象会因为这个仓库改变而改变,因此深度克隆很有必要
例子:
var obj = {
name :'wzy',
sex:'male',
age:24,
card:[5,'zy'],
dasd:{
a:888,
d:'gg'
}
}
var obj1 = {}
function deepClone(origin,target){
var target = target || {},
toStr = Object.prototype.toString,
arrStr = '[object Array]';
for(var prop in origin){
if(origin.hasOwnProperty(prop)){
if (origin[prop] !== 'null' &&typeof(origin[prop]) == 'object'){
// if(toStr.call(origin[prop])==arrStr){
// target[prop]=[];
// }else{
// target[prop]={};
// }
// 用三目运算符可以简洁代码判断
target[prop]=(toStr.call(origin[prop])==arrStr)? [] :{};
deepClone(origin[prop],target[prop]);
}else{
target[prop] = origin[prop];
}
}
}
return target;
}
ps:(在后台验证时…)对象{}是不能直接.push方法的,要在对象里手动添加。数组[ ]倒是可以直接用.push方法
上面的例子中,obj有对象和数组,但是用了深度克隆就不会互相影响了
总结:
克隆前来要判断,
原始引用要区分。
克隆好来五步走,
一步不缺无BUG。