JS中的对象
js中的数据分为两大类:
原始类型和对象类型。
- 原始类型:
- 数值
- 字符串
- 布尔值
- Null
- Undefined
- ECMA6里面还多了个Symbol,不太了解的说
- 对象类型
-
- 函数(js中的一等对象)
- 数组(键值的有序集合)
- 其他
既然数据类型分为两类,这两类在复制克隆时是有很大区别的。原始类型存储的是对象的实际数据,而对象类型存储的是对象的引用地址(对象的实际内容单独存放,为了减少数据开销通常存放在内存中)
ps:对象的原型也是引用对象,它把原型的方法和属性放在内存当中,通过原型链的方式来指向这个内存地址。克隆的概念
浅度克隆:原始类型为值传递,对象类型仍为引用传递
深度克隆:所有属性或元素均完全复制,与原对象完全脱离,也就是说对于新对象的修改都不会反映到原对象中。浅克隆的表现
- 1.原始类型
-
//数值克隆 var a="1"; var b=a; b="2"; console.log(a);//"1" console.log(b);//"2" //字符串克隆 var c="1"; var d=c; d="2"; console.log(c);//"1" console.log(d);//"2" //Boolean克隆 var x=true; var y=x; y=false; console.log(x);//"true" console.log(y);//"false"
原始类型即使我们采用普通的克隆方式仍能得到正确的结果,原因就是原始类型存储的是对象的实际数据。
- 对象类型
- 前面说过,函数是一等对象,当然也是对象类型,但是函数的克隆通过浅克隆即可实现。
var m=function(){ alert(1); } var n=m; n=function(){ alert(2); } console.log(m()); console.log(n());
直接通过普通赋值的方式,就实现了函数的克隆,并且不会影响之前的对象。原因就是函数的克隆会在内存单独开辟一块空间,互不影响。 现在定义一个复杂的对象类型oPerson。下面看一下对象类型的浅复制有什么危害:var oPerson={ oName:"rookiebob", oAge:"18", oAddress:{ province:"beijing" }, oFavorite:["swimming",{ reading:"history book" }], skill:function(){ console.log("bob is coding"); } }; function clone(obj){ var result = {}; for(key in obj){ result[key] = obj[key]; } return result; } var oNew = clone(oPerson); console.log(oPerson.oAddress.province);//beijing oNew.oAddress.province="shanghai"; console.log(oPerson.oAddress.province);//shanghai
经过对象克隆以后,我修改oNew的地址,发现原对象oPerson也被修改了。这说明对象的克隆不够彻底,那也就是说深度克隆失败!
深度克隆的实现
为了保证对象的所有属性都被复制到,我们必须知道,如果for循环以后,得到的元素仍是Object或者Array,那么需要再次循环,知道元素师原始类型或者函数为止。为了得到元素的类型,我们定义了一个通用函数,用来返回传入对象的类型。
function isClass(o){ if (o===null) {return Null;}; if (o===undefined) {return Undefined;}; return Object.prototype.toString.call(o).slice(8,-1); } //Object.prototype.toString.call()能直接返回对象的类属性,形如"[object class]"的字符串,我们通过截取class,就能知道传入的对象是什么类型 //深度克隆 function deepClone(obj){ var result,oClass=isClass(obj); //确定result的类型 if (oClass==="Object") { result={}; }else if(oClass==="Array"){ result=[]; }else{ return obj; } for(key in obj){ var copy = obj[key]; if (isClass(copy)=="Object") { result[key]=arguments.callee(copy);//递归调用1 }else if (isClass(copy)=="Array") { result[key]=arguments.callee(copy); }else{ result[key]=obj[key]; } } return result; } var oNew=deepClone(oPerson); oNew.oFavorite[1].reading="picture"; console.log(oNew.oFavorite[1].reading);//picture console.log(oPerson.oFavorite[1].reading);//history book oNew.oAddress.province="shanghai"; console.log(oPerson.oAddress.province);//beijing console.log(oNew.oAddress.province);//shanghai
深度克隆的对象可以完全脱离原对象,我们对新对象的任何修给都不会反映到原对象中,这样深度克隆就实现了。
PS:为什么deepClone这个函数中的result一定要判断类型?
因为,如果你的result直接是{}对象,我明明传进去的是一个数组,结果你复制完了以后,变成了一个对象了。function deepClone(obj){ var result={},oClass=isClass(obj);//不判断result类型 for(key in obj){ var copy=obj[key]; if(isClass(copy)=="Object"){ result[key]=arguments.callee(copy); }else if(isClass(copy)=="Array"){ result[key]=arguments.callee(copy); }else{ result[key]=obj[key]; } } return result; } function isClass(o){ if(o===null) return "Null"; if(o===undefined) return "Undefined"; return Object.prototype.toString.call(o).slice(8,-1); } //克隆一个数组 var arr=["a","b","c"]; var oNew=deepClone(arr); console.log(oNew);//Object {0: "a", 1: "b", 2: "c"}