深拷贝

深拷贝


深拷贝:创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”,新对象跟原对象不共享内存,修改新对象不会改到原对象。

为什么要使用深拷贝? 我们希望在改变新的数组(对象)的时候,不改变原数组(对象)。

在JS中,数据类型分为基本数据类型和引用数据类型两种,对于基本数据类型来说,它的值直接存储在栈内存中,而对于引用类型来说,它在栈内存中仅仅存储了一个引用,而真正的数据存储在堆内存中。

(1)当我们对数据进行操作的时候,会发生两种情况

基本数据类型:


var a = 3;
var b = a;
b = 5;
console.log(a); // 3
console.log(b); // 5

可以看到的是对于基本类型来说,我们将一个基本类型的值赋予 a 变量,接着将 a 的值赋予变量 b ;然后我们修改 b ;可以看到 b 被修改了,而 a 的值没有被修改,两个变量都使用的是独立的数据;

引用数据类型:


var obj1 = {
    a:  1,
    b:  2,
    c:  3
}
var obj2 = obj1;
obj2.a = 5;
console.log(obj1.a);  // 5
console.log(obj2.a);  // 5

可以看到的是,两个对象的值全部被修改了
对象是引用类型的值,对于引用类型来说,我们将 obj1 赋予 obj2 的时候,我们其实仅仅只是将 obj1 存储在栈堆中的的引用赋予了 obj2 ,而两个对象此时指向的是在堆内存中的同一个数据,所以当我们修改任意一个值的时候,修改的都是堆内存中的数据,而不是引用,所以只要修改了,同样引用的对象的值也自然而然的发生了改变。

深拷贝作用在引用类型上!例如:Object,Array
深拷贝不会拷贝引用类型的引用,而是将引用类型的值全部拷贝一份,形成一个新的引用类型,这样就不会发生引用错乱的问题,使得我们可以多次使用同样的数据,而不用担心数据之间会起冲突。

 

实现深拷贝的方法有:

①递归

使用for in 循环拷贝其值:

function ss(obj) {
// 创建一个函数
        if (typeof obj != "object" || obj == null) {
        // 判断参数如果不是对象,就返回
            return obj;
        };
        let obj1 = {};
        // 用于拷贝数据
        if (typeof obj == "object") {
        // 如果是对象进行for in 循环
            for (let i in obj) {
            // i是对象的每一个key
                obj1[i] = ss(obj[i]);
                // 进行循环拷贝, 再次调用自身,如果是对象返回循环拷贝,如果不是对象
                // 刚进入函数就会被返回
            };
            return obj2;
        }
    }

注意:递归运行效率低,次数过多的话容易造成栈溢出。

②JSON的parse和stringify方法:

先转化为字符串,在转换回来

 var obj1 = {
    a: 1,
    b: 2,
    c: 3
}
var objString = JSON.stringify(obj1);
var obj2 = JSON.parse(objString);
obj2.a = 5;
console.log(obj1.a);  // 1
console.log(obj2.a); // 5

可以看到没有发生引用问题,修改obj2的数据,并不会对obj1造成任何影响
但是使用JSON.stringify()以及JSON.parse()它是不可以拷贝 undefined , function, RegExp 等等类型。

③Jquery的$.extend:

(1)将extend函数的第一个参数设置为true:


var p = {
	"id":"007",
	"name":"刘德华",
	"wife":{
		"id":"008",
		"name":"刘德的妻子",
		"address":{
			"city":"北京",
			"area":"海淀区"
		}
	}
}
 
 
//深度拷贝的方式(把extend函数的第一个参数设置为true),把p的属性合并到jQuery($)上
 
$.extend(true,$,p);
 
p.wife.name="张三疯";
p.wife.address.city="香港";
 
console.log($.id);
console.log($.wife.name);
console.log($.wife.address.city);
 
console.log(p);

(2)extend函数里不传入第一个参数,默认为false:

//二、浅拷贝的方式(extend函数里不传入第一个参数,默认为false),把p的属性合并到jQuery($)上,但是共享引用类型的属性wife和address
 
var p = {
	"id":"007",
	"name":"刘德华",
	"wife":{
		"id":"008",
		"name":"刘德的妻子",
		"address":{
			"city":"北京",
			"area":"海淀区"
		}
	}
}
$.extend($,p);//或者 $.extend(p);
 
p.wife.name="张三疯";
p.wife.address.city="香港";
 
console.log($.id);
console.log($.wife.name);
console.log($.wife.address.city);
 
console.log(p);
 

注:$.extend参数:
第一个参数是布尔值,是否深复制
第二个参数是目标对象,其他对象的成员属性将被附加到该对象上
第三个及以后的参数是被合并的对象

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值