深度复制,浅层复制

  1 javascript中对象的深度克隆
  2 2014-09-13 08:43:52             收藏   我要投稿
  3 又是一个月多月没有更新博客了,这段时间回学校处理下论文的事情,实习的生活也暂时告一段落(在公司上班,才发现学校里面的生活简直如天堂一般,相信很多已经毕业的小伙伴肯定被我说中了,说中了请给本文点个赞,哈哈!)。希望接下来自己的更新进度能加快,马上又是一年校招时,被虐也好、大牛虐别人也罢,总之祝福各位今年要找工作的小伙伴们好运。那么,今天就聊一下一个常见的笔试、面试题,js中对象的深度克隆。翻了下这个题目,在很多地方出现过,已经算一个老的题目了,但是每年的校招中总会考到,其实想想,这个题目考查的知识点还是蛮多的,尤其是对基础知识的考查。好了,闲话不多说,开始正题。
  4  
  5 
  6 一、js中的对象
  7 
  8  
  9 
 10 谈到对象的克隆,必定要说一下对象的概念。
 11 
 12  
 13 
 14 js中的数据类型分为两大类:原始类型和对象类型。(1)原始类型包括:数值、字符串、布尔值、null、undefined(后两个是特殊的原始值,这里不做详细的说明,我的上一篇博客有谈到过一些)(2)对象类型包括:对象即是属性的集合,当然这里又两个特殊的对象----函数(js中的一等对象)、数组(键值的有序集合)。
 15 
 16  
 17 
 18 好了既然对象分为这两类,这两种类型在复制克隆的时候是有很大区别的。原始类型存储的是对象的实际数据,而对象类型存储的是对象的引用地址(对象的实际内容单独存放,为了减少数据开销通常存放在内存中)。ps:说到这里,大家要知道,对象的原型也是引用对象,它把原型的方法和属性放在内存当中,通过原型链的方式来指向这个内存地址。
 19 
 20  
 21 
 22 二、克隆的概念
 23 
 24  
 25 
 26 浅度克隆:原始类型为值传递,对象类型仍为引用传递。
 27 
 28  
 29 
 30 深度克隆:所有元素或属性均完全复制,与原对象完全脱离,也就是说所有对于新对象的修改都不会反映到原对象中。
 31 
 32  
 33 
 34 三、浅克隆的表现
 35 
 36  
 37 
 38 1,原始类型
 39 
 40  
 41 
 42 看下面一段代码:
 43 
 44  
 45 
 46 复制代码
 47 
 48 //数值克隆的表现
 49 
 50 var a="1";
 51 
 52 var b=a;
 53 
 54 b="2";
 55 
 56 console.log(a);// "1"
 57 
 58 console.log(b);// "2"
 59 
 60  
 61 
 62 //字符串克隆的表现
 63 
 64 var c="1";
 65 
 66 var d=c;
 67 
 68 d="2";
 69 
 70 console.log(c);// "1"
 71 
 72 console.log(d);// "2"
 73 
 74  
 75 
 76 //字符串克隆的表现
 77 
 78 var x=true;
 79 
 80 var y=x;
 81 
 82 y=false;
 83 
 84 console.log(x);// true
 85 
 86 console.log(y);// false
 87 
 88 复制代码
 89 
 90 从上面的代码大家可以看出,原始类型即使我们采用普通的克隆方式仍能得到正确的结果,原因就是原始类型存储的是对象的实际数据。
 91 
 92  
 93 
 94 2.对象类型
 95 
 96  
 97 
 98 前面说过,函数式一等对象,当然也是对象类型,但是函数的克隆通过浅克隆即可实现
 99 
100  
101 
102 复制代码
103 
104 var m=function(){alert(1);};
105 
106 var n=m;
107 
108 n=function(){alert(2);};
109 
110  
111 
112 console.log(m());//1
113 
114 console.log(n());//2
115 
116 复制代码
117 
118 大家能看到,我们直接通过普通赋值的方式,就实现了函数的克隆,并且不会影响之前的对象。原因就是函数的克隆会在内存单独开辟一块空间,互不影响。
119 
120  
121 
122 好了,说了这个特殊的”关系户“以后,我们来说说普通的”选手“。为了方便后续的代码表现,我这里定义一个复杂的对象类型oPerson。下面看一下对象类型的浅复制有什么危害:
123 
124  
125 
126 复制代码
127 
128 var oPerson={
129 
130     oName:"rookiebob",
131 
132     oAge:"18",
133 
134     oAddress:{
135 
136         province:"beijing"
137 
138     },    
139 
140     ofavorite:[
141 
142         "swimming",
143 
144         {reading:"history book"}
145 
146     ],
147 
148     skill:function(){
149 
150         console.log("bob is coding");
151 
152     }
153 
154 };
155 
156 function clone(obj){
157 
158     var result={};
159 
160     for(key in obj){
161 
162         result[key]=obj[key];
163 
164     }
165 
166     return result;
167 
168 }
169 
170 var oNew=clone(oPerson);
171 
172 console.log(oPerson.oAddress.province);//beijing
173 
174 oNew.oAddress.province="shanghai";
175 
176 console.log(oPerson.oAddress.province);//shanghai
177 
178 复制代码
179 
180 通过上面的代码,大家能看到,经过对象克隆以后,我修改oNew的地址,发现原对象oPerson也被修改了。这说明对象的克隆不够彻底,那也就是说深度克隆失败!
181 
182  
183 
184 四、深克隆的实现
185 
186  
187 
188 为了保证对象的所有属性都被复制到,我们必须知道如果for循环以后,得到的元素仍是Object或者Array,那么需要再次循环,直到元素是原始类型或者函数为止。为了得到元素的类型,我们定义一个通用函数,用来返回传入对象的类型。
189 
190  
191 
192 复制代码
193 
194 //返回传递给他的任意对象的类
195 
196 function isClass(o){
197 
198     if(o===null) return "Null";
199 
200     if(o===undefined) return "Undefined";
201 
202     return Object.prototype.toString.call(o).slice(8,-1);
203 
204 }
205 
206 复制代码
207 
208 PS:Object.prototype.toString.call(o)能直接返回对象的类属性,形如"[object class]"的字符串,我们通过截取class,并能知道传入的对象是什么类型。
209 
210  
211 
212 当然这里有两个疑问需要解释下:
213 
214  
215 
216 (1)为什么不直接用toString方法?这是为了防止对象中的toString方法被重写,为了正确的调用toString()版本,必须间接的调用Function.call()方法
217 
218  
219 
220 (2)为什么不使用typeof来直接判断类型?因为对于Array而言,使用typeof(Array)返回的是object,所以不能得到正确的Array,这里对于后续的数组克隆将产生致命的问题。
221 
222  
223 
224 万事俱备,只欠曹操了,下面就正儿八经的开始克隆。
225 
226  
227 
228 复制代码
229 
230 //深度克隆
231 
232 function deepClone(obj){
233 
234     var result,oClass=isClass(obj);
235 
236         //确定result的类型
237 
238     if(oClass==="Object"){
239 
240         result={};
241 
242     }else if(oClass==="Array"){
243 
244         result=[];
245 
246     }else{
247 
248         return obj;
249 
250     }
251 
252     for(key in obj){
253 
254         var copy=obj[key];
255 
256         if(isClass(copy)=="Object"){
257 
258             result[key]=arguments.callee(copy);//递归调用
259 
260         }else if(isClass(copy)=="Array"){
261 
262             result[key]=arguments.callee(copy);
263 
264         }else{
265 
266             result[key]=obj[key];
267 
268         }
269 
270     }
271 
272     return result;
273 
274 }
275 
276 //返回传递给他的任意对象的类
277 
278 function isClass(o){
279 
280     if(o===null) return "Null";
281 
282     if(o===undefined) return "Undefined";
283 
284     return Object.prototype.toString.call(o).slice(8,-1);
285 
286 }
287 
288 var oPerson={
289 
290     oName:"rookiebob",
291 
292     oAge:"18",
293 
294     oAddress:{
295 
296         province:"beijing"
297 
298     },    
299 
300     ofavorite:[
301 
302         "swimming",
303 
304         {reading:"history book"}
305 
306     ],
307 
308     skill:function(){
309 
310         console.log("bob is coding");
311 
312     }
313 
314 };
315 
316 //深度克隆一个对象
317 
318 var oNew=deepClone(oPerson);
319 
320  
321 
322 oNew.ofavorite[1].reading="picture";
323 
324 console.log(oNew.ofavorite[1].reading);//picture
325 
326 console.log(oPerson.ofavorite[1].reading);//history book
327 
328  
329 
330 oNew.oAddress.province="shanghai";
331 
332 console.log(oPerson.oAddress.province);//beijing
333 
334 console.log(oNew.oAddress.province);//shanghai
335 
336 复制代码
337 
338 从上面的代码可以看到,深度克隆的对象可以完全脱离原对象,我们对新对象的任何修改都不会反映到原对象中,这样深度克隆就实现了。
339 
340  
341 
342 这里要注意一点的就是:为什么deepClone这个函数中的result一定要判断类型?这里有一种情况,如果你的result直接是{}对象,我明明传进去的是一个数组,结果你复制完了以后,变成了一个对象了。
343 
344  
345 
346 复制代码
347 
348 //深度克隆
349 
350 function deepClone(obj){
351 
352     var result={},oClass=isClass(obj);
353 
354     // if(oClass==="Object"){
355 
356     //     result={};
357 
358     // }else if(oClass==="Array"){
359 
360     //     result=[];
361 
362     // }else{
363 
364     //     return obj;
365 
366     // }
367 
368     for(key in obj){
369 
370         var copy=obj[key];
371 
372         if(isClass(copy)=="Object"){
373 
374             result[key]=arguments.callee(copy);
375 
376         }else if(isClass(copy)=="Array"){
377 
378             result[key]=arguments.callee(copy);
379 
380         }else{
381 
382             result[key]=obj[key];
383 
384         }
385 
386     }
387 
388     return result;
389 
390 }
391 
392 function isClass(o){
393 
394     if(o===null) return "Null";
395 
396     if(o===undefined) return "Undefined";
397 
398     return Object.prototype.toString.call(o).slice(8,-1);
399 
400 }
401 
402 //克隆一个数组
403 
404 var arr=["a","b","c"];
405 
406 var oNew=deepClone(arr);
407 
408 console.log(oNew);//Object {0: "a", 1: "b", 2: "c"} 

 

posted @ 2019-08-06 19:08  最好的安排  阅读(156)  评论(0编辑  收藏  举报

Knowledge is infinite