一、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

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

PS:对象的简易克隆小技巧:var tmpObj = JSON.parse(JSON.stringify(传入对象));

  

2.对象类型

  前面说过,函数式一等对象,当然也是对象类型,但是函数的克隆通过浅克隆即可实现

  1. var m=function(){alert(1);};
  2. var n=m;
  3. n=function(){alert(2);};
  4. console.log(m());//1
  5. console.log(n());//2

 大家能看到,我们直接通过普通赋值的方式,就实现了函数的克隆,并且不会影响之前的对象。原因就是函数的克隆会在内存单独开辟一块空间,互不影响。

  好了,说了这个特殊的”关系户“以后,我们来说说普通的”选手“。为了方便后续的代码表现,我这里定义一个复杂的对象类型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. return Object.prototype.toString.call(o).slice(8,-1);
  4. }

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. oNew.ofavorite[1].reading="picture";
  47. console.log(oNew.ofavorite[1].reading);//picture
  48. console.log(oPerson.ofavorite[1].reading);//history book
  49. oNew.oAddress.province="shanghai";
  50. console.log(oPerson.oAddress.province);//beijing
  51. 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"}

作为知识点记录,转载自:http://www.2cto.com/kf/201409/332955.html

js之深度克隆、简易克隆的更多相关文章

  1. JS做深度学习3——数据结构

    最近在上海上班了,很久没有写博客了,闲下来继续关注和研究Tensorflow.js 关于深度学习的文章我也已经写了不少,部分早期作品可能包含了不少错误的认识,在后面的博文中会改进或重新审视. 今天聊聊 ...

  2. UIWebView与JS的深度交互

    我要实现这样一个需求:按照本地的CSS文件展示一串网络获取的带HTML格式的只有body部分的文本,需要自己拼写完整的 HTML.除此之外,还需要禁用获取的HTML文本中自带的 < img &g ...

  3. UIWebView与JS的深度交互-b

    要实现这样一个需求:按照本地的CSS文件展示一串网络获取的带HTML格式的只有body部分的文本,需要自己拼写完整的 HTML.除此之外,还需要禁用获取的HTML文本中自带的 < img > ...

  4. JS做深度学习2——导入训练模型

    JS做深度学习2--导入训练模型 改进项目 前段时间,我做了个RNN预测金融数据的毕业设计(华尔街),当时TensorFlow.js还没有发布,我不得已使用了keras对数据进行了训练,并且拟合好了不 ...

  5. JS做深度学习1——偶然发现与入门

    JS做深度学习1--偶然发现与入门 不久前,我初次涉猎了Node.js,并且使用它开发了毕业设计的WEB模块,然后通过在Node中调用系统命令执行Python文件方式实现了深度学习功能模块的对接,Py ...

  6. [JS]使用JavaScript实现简易俄罗斯方块

    [JS]使用JavaScript实现简易俄罗斯方块 首先,大家可以点击此处来预览一下游戏效果,随后将会以此为模板讲解如何使用JavaScript实现这样一个简易的俄罗斯方块项目(以下简称"该 ...

  7. JS的深度克隆,利用构造函数原型深度克隆

    我爱撸码,撸码使我感到快乐!大家好,我是Counter.今天来写写,JS中的深度克隆,这个在笔面试中,考的还是比较多的,主要是对象与数组的赋值,如果直接赋值的话,那么得到的是对象或者数组在堆里的地址, ...

  8. JS对象深度克隆

    首先看一个例子: var student = { name:"yxz", age:25 } var newStudent = student; newStudent.sex = & ...

  9. 原生js实现深度克隆

    总体思路: 判断对象当中的值为引用值还是原始值 如果是引用值,判断是数组还是对象,如果是原始值直接copy 递归 注意:不要忘了排除null,因为typeof null = 'object' func ...

随机推荐

  1. python find()用法

    案例: ### 1 ### str = "01213456" if str.find("23"): print "YES!" else: p ...

  2. stm32之开发入门

    一.开发环境配置 在开发stm32应用之前,我们需要先配置好开发环境. 首先从keil官网下载keil MDK-ARM软件包(v5版本与v4版本不同,v5版本需要下载额外的stm32芯片包)和芯片包( ...

  3. [poj2019]Cornfields(二维RMQ)

    题意:给你一个n*n的矩阵,让你从中圈定一个小矩阵,其大小为b*b,有q个询问,每次询问告诉你小矩阵的左上角,求小矩阵内的最大值和最小值的差. 解题关键:二维st表模板题. 预处理复杂度:$O({n^ ...

  4. 浏览器原生 form 表单POST 数据的两种方式

    我们在提交表单的时候,form表单参数中会有一个enctype的参数.enctype指定了HTTP请求的Content-Type. 常用有两种:application/x-www-form-urlen ...

  5. Apache2.2安装图解

    Apache2.2安装图解 2010-12-14 15:32:44|  分类: 不学无术之杂 |  标签:安装  端口  httpd  apache2.2  服务器   |字号 订阅 Apache音译 ...

  6. day1 java基础回顾- 文件路径

    绝对路径 以根目录或某盘符开头的路径(或者说完整的路径) 例如: l  c:/a.txt (Windows操作系统中) l  c:/xxx/a.txt (Windows操作系统中) l  /var/x ...

  7. ASP.NET学习笔记(四)CDOSYS邮件

    使用 CDOSYS 发送电子邮件 CDO (Collaboration Data Objects) 是一项微软的技术,设计目的是用来简化通信程序的创建. CDOSYS 是 ASP 中的内置组件.我们会 ...

  8. 在 Ubuntu 上安装 Protobuf 3

    什么时候需要安装 如果使用 protoc 命令,遇到 Protoc not found,表示未安装.或者,执行时出现错误:This parser only recognizes "proto ...

  9. 如何运用多阶构建编写优雅的Dockerfile

    导读 Kubernetes要从容器化开始,而容器又需要从Dockerfile开始,本文将介绍如何写出一个优雅的Dockerfile文件. 文章主要内容包括: Docker容器 Dockerfile 使 ...

  10. IT兄弟连 Java语法教程 Java开发环境 安装JDK

    因为我们要开发Java程序,所以必须在我们的计算机中安装Sun(Oracle)公司提供给我们的JDK.目前最新版本的JDK是JDK 10,但是我们以学习JDK 8为主,所以我们要安装的版本是JDK 8 ...