记录一个常见的面试题,javascript中对象的深度克隆,转载自:http://www.2cto.com/kf/201409/332955.html

  今天就聊一下一个常见的笔试、面试题,js中对象的深度克隆。翻了下这个题目,在很多地方出现过,已经算一个老的题目了,但是每年的校招中总会考到,其实想想,这个题目考查的知识点还是蛮多的,尤其是对基础知识的考查。好了,闲话不多说,开始正题。

  

一、js中的对象
 
  谈到对象的克隆,必定要说一下对象的概念。
 
  js中的数据类型分为两大类:原始类型和对象类型。
    (1)原始类型包括:数值、字符串、布尔值、null、undefined(后两个是特殊的原始值,这里不做详细的说明,我的上一篇博客有谈到过一些)
    (2)对象类型包括:对象即是属性的集合,当然这里又两个特殊的对象----函数(js中的一等对象)、数组(键值的有序集合)。
 
  好了既然对象分为这两类,这两种类型在复制克隆的时候是有很大区别的。原始类型存储的是对象的实际数据,而对象类型存储的是对象的引用地址(对象的实际内容单独存放,为了减少数据开销通常存放在内存中)。ps:说到这里,大家要知道,对象的原型也是引用对象,它把原型的方法和属性放在内存当中,通过原型链的方式来指向这个内存地址。
 
二、克隆的概念
 
  浅度克隆:原始类型为值传递,对象类型仍为引用传递。
 
  深度克隆:所有元素或属性均完全复制,与原对象完全脱离,也就是说所有对于新对象的修改都不会反映到原对象中。
 
三、浅克隆的表现
 
1,原始类型
 
  看下面一段代码:
  1. //数值克隆的表现
  2. var a="1";
  3. var b=a;
  4. b="2";
  5. console.log(a);// "1"
  6. console.log(b);// "2"
  1. //字符串克隆的表现
  2. var c="1";
  3. var d=c;
  4. d="2";
  5. console.log(c);// "1"
  6. console.log(d);// "2"
  1. //字符串克隆的表现
  2. var x=true;
  3. var y=x;
  4. y=false;
  5. console.log(x);// true
  6. console.log(y);// false

  从上面的代码大家可以看出,原始类型即使我们采用普通的克隆方式仍能得到正确的结果,原因就是原始类型存储的是对象的实际数据。

 
2.对象类型
 
  前面说过,函数式一等对象,当然也是对象类型,但是函数的克隆通过浅克隆即可实现
  1. var m=function(){alert(1);};
  2. var n=m;
  3. n=function(){alert(2);};
  4.  
  5. console.log(m());//
  6. console.log(n());//
  大家能看到,我们直接通过普通赋值的方式,就实现了函数的克隆,并且不会影响之前的对象。原因就是函数的克隆会在内存单独开辟一块空间,互不影响。
 
  好了,说了这个特殊的”关系户“以后,我们来说说普通的”选手“。为了方便后续的代码表现,我这里定义一个复杂的对象类型oPerson。下面看一下对象类型的浅复制有什么危害:
  1. var oPerson={
  2. oName:"rookiebob",
  3. oAge:"18",
  4. oAddress:{
  5. province:"beijing"
  6. },
  7. ofavorite:[
  8. "swimming",
  9. {reading:"history book"}
  10. ],
  11. skill:function(){
  12. console.log("bob is coding");
  13. }
  14. };
  15. function clone(obj){
  16. var result={};
  17. for(key in obj){
  18. result[key]=obj[key];
  19. }
  20. return result;
  21. }
  22. var oNew=clone(oPerson);
  23. console.log(oPerson.oAddress.province);//beijing
  24. oNew.oAddress.province="shanghai";
  25. console.log(oPerson.oAddress.province);//shanghai
  通过上面的代码,大家能看到,经过对象克隆以后,我修改oNew的地址,发现原对象oPerson也被修改了。这说明对象的克隆不够彻底,那也就是说深度克隆失败!
 
四、深克隆的实现
 
  为了保证对象的所有属性都被复制到,我们必须知道如果for循环以后,得到的元素仍是Object或者Array,那么需要再次循环,直到元素是原始类型或者函数为止。为了得到元素的类型,我们定义一个通用函数,用来返回传入对象的类型。
  1. //返回传递给他的任意对象的类
  2. function isClass(o){
  3. if(o===null) return "Null";
  4. if(o===undefined) return "Undefined";
  5. return Object.prototype.toString.call(o).slice(8,-1);
  6. }
  PS:Object.prototype.toString.call(o)能直接返回对象的类属性,形如"[object class]"的字符串,我们通过截取class,并能知道传入的对象是什么类型。
 
当然这里有两个疑问需要解释下:
  
 
  (1)为什么不直接用toString方法?这是为了防止对象中的toString方法被重写,为了正确的调用toString()版本,必须间接的调用Function.call()方法
 
  (2)为什么不使用typeof来直接判断类型?因为对于Array而言,使用typeof(Array)返回的是object,所以不能得到正确的Array,这里对于后续的数组克隆将产生致命的问题。
 
下面就是真正的深度克隆
  1. //深度克隆
  2. function deepClone(obj){
  3. var result,oClass=isClass(obj);
  4. //确定result的类型
  5. if(oClass==="Object"){
  6. result={};
  7. }else if(oClass==="Array"){
  8. result=[];
  9. }else{
  10. return obj;
  11. }
  12. for(key in obj){
  13. var copy=obj[key];
  14. if(isClass(copy)=="Object"){
  15. result[key]=arguments.callee(copy);//递归调用
  16. }else if(isClass(copy)=="Array"){
  17. result[key]=arguments.callee(copy);
  18. }else{
  19. result[key]=obj[key];
  20. }
  21. }
  22. return result;
  23. }
  24. //返回传递给他的任意对象的类
  25. function isClass(o){
  26. if(o===null) return "Null";
  27. if(o===undefined) return "Undefined";
  28. return Object.prototype.toString.call(o).slice(8,-1);
  29. }
  30. var oPerson={
  31. oName:"rookiebob",
  32. oAge:"18",
  33. oAddress:{
  34. province:"beijing"
  35. },
  36. ofavorite:[
  37. "swimming",
  38. {reading:"history book"}
  39. ],
  40. skill:function(){
  41. console.log("bob is coding");
  42. }
  43. };
  44. //深度克隆一个对象
  45. var oNew=deepClone(oPerson);
  46.  
  47. oNew.ofavorite[1].reading="picture";
  48. console.log(oNew.ofavorite[1].reading);//picture
  49. console.log(oPerson.ofavorite[1].reading);//history book
  50.  
  51. oNew.oAddress.province="shanghai";
  52. console.log(oPerson.oAddress.province);//beijing
  53. console.log(oNew.oAddress.province);//shanghai
  从上面的代码可以看到,深度克隆的对象可以完全脱离原对象,我们对新对象的任何修改都不会反映到原对象中,这样深度克隆就实现了。
 
  这里要注意一点的就是:为什么deepClone这个函数中的result一定要判断类型?这里有一种情况,如果你的result直接是{}对象,我明明传进去的是一个数组,结果你复制完了以后,变成了一个对象了。
  1. //深度克隆
  2. function deepClone(obj){
  3. var result={},oClass=isClass(obj);
  4. // if(oClass==="Object"){
  5. // result={};
  6. // }else if(oClass==="Array"){
  7. // result=[];
  8. // }else{
  9. // return obj;
  10. // }
  11. for(key in obj){
  12. var copy=obj[key];
  13. if(isClass(copy)=="Object"){
  14. result[key]=arguments.callee(copy);
  15. }else if(isClass(copy)=="Array"){
  16. result[key]=arguments.callee(copy);
  17. }else{
  18. result[key]=obj[key];
  19. }
  20. }
  21. return result;
  22. }
  23. function isClass(o){
  24. if(o===null) return "Null";
  25. if(o===undefined) return "Undefined";
  26. return Object.prototype.toString.call(o).slice(8,-1);
  27. }
  28. //克隆一个数组
  29. var arr=["a","b","c"];
  30. var oNew=deepClone(arr);
  31. console.log(oNew);//Object {0: "a", 1: "b", 2: "c"}

javascript中对象的深度克隆的更多相关文章

  1. JavaScript实现对象的深度克隆及typeof和instanceof【简洁】【分享】

    JavaScript实现对象的深度克隆 代码实现如下: <!DOCTYPE html> <html lang="en"> <head> < ...

  2. javascript对象的深度克隆

    在做项目的时候需要向对象里面添加新属性,又不想修改原对象.于是就写: var newObj = oldObj,但是新对象属性改变后就对象也会跟着改变,这是因为无论是新对象还是旧对象,指向的内存地址都是 ...

  3. js对象的深度克隆

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. JAVA对象的深度克隆

    有时候,我们需要把对象A的所有值复制给对象B(B = A),但是这样用等号给赋值你会发现,当B中的某个对象值改变时,同时也会修改到A中相应对象的值! 也许你会说,用clone()不就行了?!你的想法只 ...

  5. javascript中对象字面量的理解

    javascript中对象字面量与数组字面量 第一部分 我们知道JavaScript中的数据类型有基本数据类型和引用类型,其中Object类型就是非常常用的类型.那么如果创建一个Object类型的实例 ...

  6. 关于JavaScript中对象的继承实现的学习总结

    一.原型链 JavaScript 中原型链是实现继承的主要方法.其主要的思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.实现原型链有一种基本模式,其代码如下. function Super ...

  7. MyDAL - 引用类型对象 .DeepClone() 深度克隆[深度复制] 工具 使用

    索引: 目录索引 一.API 列表 .DeepClone() 用于 Model / Entity / ... ... 等引用类型对象的深度克隆 特性说明 1.不需要对对象做任何特殊处理,直接 .Dee ...

  8. JavaScript中对象的属性

    在JavaScript中,属性决定了一个对象的状态,本文详细的研究了它们是如何工作的. 属性类型 JavaScript中有三种不同类型的属性:命名数据属性(named data properties) ...

  9. JavaScript中对象转换为原始值的规则

    JavaScript中对象转换为原始值遵循哪些原则? P52 对象到布尔值对象到布尔值的转换非常简单:所有的对象(包括数字和函数)都转换为true.对于包装对象亦是如此:new Boolean(fal ...

随机推荐

  1. mac上卸载oracle jdk 1.8.0_31

    mac上卸载oracle jdk 1.8.0_31版本,因为版本太高了.得安装旧版本才行.卸载的顺序是:进入finder,然后点应用程序,按command+向上箭头键,分别进入根目录的系统与资源库找到 ...

  2. mongoDB(3) mapReduce

    mapReduce是大数据的核心内容,但实际操作中别用这个,所谓的mapReduce分两步 1.map:将数据分别取出,Map函数调用emit(key,value)遍历集合中所有的记录,将key与va ...

  3. (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

    (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 在我们RDIFramework.NET代码生成器中,有这样一个应用,就是通过数据库表自动生成表的CREA ...

  4. mysql安装配置问题(linux下)

    1.安装后使用:mysql -u root -p 无法登录mysql 错误提示:ERROR 1045 (28000): Access denied for user 'root'@'localhost ...

  5. Spring+Mybatis+SpringMVC+Maven+MySql搭建实例

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了如何使用Maven来搭建Spring+Mybatis+SpringMVC+M ...

  6. linux--------wdcp中的各种坑。

    1.刚买的空间客服给安装了wdcplinux,结果上去一看PHP是5.2版本的,这不是搞笑嘛.然后就有了下面的升级: 复制这条命令回车然后敲Y就可以: wget http://soft.itbulu. ...

  7. html5,进度条

    <form action="" id="myform">        <progress value="20" max= ...

  8. Oracle常用日期函数

    常用的时间格式掩码如下:掩码元素       含义YYYY           四位数年份 (如:2005)     yearYY             二位数年份(如  05) Q         ...

  9. 让keepalived监控NginX的状态

    经过前面的配置,如果主服务器的keepalived停止服务,从服务器会自动接管VIP对外服务:一旦主服务器的keepalived恢复,会重新接管VIP. 但这并不是我们需要的,我们需要的是当NginX ...

  10. C#网络爬虫

    CronMaker is a utility which helps you to build cron expressions. CronMaker uses Quartz open source ...