先从一个问题开始说起,请问foo.x的值是?
//常见的面试题,考虑垃圾回收机制
var a={n:1};
a.x=a={n:2};
console.log(a.x); //undefined
console.log(a); //{n:2}
var a={n:1};
a.x={n:2};
console.log(a.x); //{n:2}
1.在执行前,会先将a和a.x中的a的引用地址都取出来,此值他们都指向{n:1}
2.在内存中创建一个新对象{n:2}
3.执行a={n:2},将a的引用从指向{n:1}改为指向新的{n:2}
4.执行a.x=a,此时a已经指向了新对象,而a.x因为在执行前保留了原引用,所以a.x的a依然指向原先的{n:1}对象,所以给原对象新增一个属性x,内容为{n:2}也就是现在a
5.语句执行结束,原对象由{n:1}变成{n:1,x:{n:2}},而原对象因为无人再引用他,所以被GC回收,当前a指向新对象{n:2}
6.所以就有了文章开头的运行结果,再执行a.x,自然就是undefined了;
1.深拷贝和浅拷贝的区别?
深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。
浅复制 —-只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。
深复制 —-在计算机中开辟了一块新的内存地址用于存放复制的对象。
2.浅拷贝实例
//此递归方法不包含数组对象
var obj = { a:1, arr: [2,3] };
var shallowObj = shallowCopy(obj);
function shallowCopy(src) {
var newobj = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
newobj[prop] = src[prop];
}
}
return newobj;
}
因为浅复制只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,所以浅复制会导致 obj.arr 和 shallowObj.arr 指向同一块内存地址,大概的示意图如下。
导致的结果就是,修改其中一个的值,另一个也会被改变:
shallowObj.arr[1] = 5;
console.log(obj.arr[1]); // 5
3.深拷贝实例
而深复制则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。这就不会存在上面 obj 和 shallowObj 的 arr 属性指向同一个对象的问题。
var obj = { a:1, arr: [1,2], nation : '中国', birthplaces:['北京','上海','广州'] };
var obj2 = {name:'杨'};
obj2 = deepCopy(obj,obj2);
console.log(obj2); //深复制,要想达到深复制就需要用递归
function deepCopy(o,c){
var c = c || {};
for(var i in o){
if(typeof o[i] === 'object'){ //要考虑深复制问题了
if(o[i].constructor === Array){
c[i] =[]; //这是数组
}else{
c[i] = {}; //这是对象
}
deepCopy(o[i],c[i]);
}else{
c[i] = o[i];
}
}
return c
}
结果如下面的示意图所示:
这样obj和obj2分别拥有不同的内存地址,两边值的改变互不影响。
4.jQuery中的深拷贝方法介绍?
深拷贝也可以使用JQuery的extend方法,注意使用合并返回值;
let newObj = $.extend(true,{},oldObj);
let oldObj={"name":"test","age":18,"info":{"height":180,"weight":63}};
let newObj = $.extend(true,{},oldObj);
console.log(newObj);
newObj.name="liuhehua";
console.log(oldObj.name); //test
console.log(newObj.name); //liudehua