首先我们来说说什么是拷贝:就是复制的同时加上了传值。
然后问题就来了什么是有深度的什么是浅度的,在想要了解我们这个问题之前我们先来了解一下下面的一个知识点
基本类型传递,引用类型传递
首先我们来看下基本类型传递:就是基本数据类型之间的数据传递,什么是基本数据类型呢?string,number,boolean,null,undefined
他们这些类型的值发生传递的时候是将自己复制了一份,吧赋值的哪一份传递给了莫一个变量;
引用类型传递:就是引用类型的值的的一个传递,那么什么是引用类型呢?就是对象(object,Arrey,RegExp,function),他们都是对象,对象之间的赋值就不会吧自己复制一份,为什么呢?你想想假设每一个对象都是一个很大的房间,我们要把所有房间里的东西都复制一份,和房间里面的小物品都要按原来的方式摆好来是不是太耗费时间了,所以在对象之间的传输,只是赋值了钥匙(指针),就是我们都有了这个房间的钥匙,所以就会出现大家可能知道的引用类型之间的传递的时候其中一个改变,另一个也会发生改变
当你们看懂了上面的之后我们就来分析一下上面是深度的什么是浅度的
浅拷贝是拷贝一层,深层次的对象级别的就拷贝引用;深拷贝是拷贝多层,每一级别的数据都会拷贝出来;‘/
下面我们就来些一些方法来看下如何实现深度拷贝?
那么如何实现呢?可能大家一时间想不起来?那就对了,那就看下下面的这个例子
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- b 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }
有人看了就奇怪了为什么这样就没关系了呢?为什么?想一想?
对没错就是这样:因为obj1.a 这个值它是一个基本类型,当我们在一次改变这个obj1.a这些基本类型值的时候是改变的他们的复制的副本,所以就没关系了?对不对啊?
没错?其实还是有一定问题?什么问题呢?如果我们ibj1.a他还是一个对象,那么他就还是一个引用类型那么我们不就还是不可以吗对把?嗯是的还是不可以?
那么我们需要干上面呢?就是要把对象改为基本类型;
var obj = {}
var a = {
b : 10;
c : {
d : 11,
e: 13,
},
}
这个例子中:如是可以的obj.b = a.b,但是obj.c = a.c 这个是不可以的因为a.c还是一个对象,那么现在obj.c还是一个钥匙,他改变了值,原来的a对象还是会改变,所以a.c这个对象还要在分变成obj.c.d = a.c.d 这样就可以了,那么怎么分呢?用递归
function deepClone(initaObj , finalObj){
var obj = finalObj || {};
for(var i in initaObj){
if(typeof initaObj[i] === 'object'){
obj[i] = (initaObj[i].constructor === Array) ? [] : {};
arguments.callee(initaObj[i],obj);
}else{
obj[i] = initaObj[i];
}
}
return obj;
}
arguments.callee():表示调用的函数本身,为什么用这个不用deepClone()这个呢?减少耦合性,当要改变函数名的时候,就可以少改一次
这样就可以达到把对象中的属性值中的对象在分一遍?
但是你们有没有想过代码中的一些问题
1.obj[i] = (initaObj[i].constructor === Array) ? [] : {}; 这一行代码的作用是什么
我来告诉大家因为有可能属性值是一个数组,为了确保拷贝是一样就这样了
2.输出的值是什么
var obj = {};
var a = {
a : 1,
b : {
c : 11,
d : 111,
},
e : [2,3,4,5]
}
console.log(deepClone(a,obj));
假设加上什么的代码执行会输出和和a对象一样的值,看上去对了,我们的目的就是要复制一个和他一样的值,但是他们虽然他们值是一样的但是可以确保他们不是同一给房子,而是我们需要的两给房子呢?我们只需要改变obj中的属性值看下a中的会不会也随着发生改变就可以了,答案是不会的( 测试下),所以是对了
{a: 1, b: {…}, e: Array(4)}
a: 2
b: {c: 11, d: 111}
e: (4) [2, 3, 4, 5]
__proto__: Object
obj.a = 2
a.a = 1
其实也就是每下项都是一基本类型的值传给新的对象,但是是一层一层的传,同时要注意是不是数组,(其实还有其他的但是目前不考虑,比如函数)
3.下一个问题就是还有一个情况就是遇到了属性值是函数function怎么办?答案是可以的
为什么呢?我把我的理解写一下:函数的这个房间,和数组和对象的区别是函数这个房间他是不可以改变,他会自己运行完了自己毁灭,防止内存泄漏(占了太多没用的内存,整体的内存泄漏了)
首先 typeof function !== object 而是等于 Function 运行上面的函数obj[i] = initaObj[i];那么新的对象你某一个属性他也会变成了一个(钥匙)指针,这个函数的房间是没办法去改变的,你去改变只是换了给钥匙,或者说是变成了基本类型的值,原来需要给你复制的对象的某一个属性,它还是拿的打开那个房间的钥匙(还是那个函数的指针,指向那个函数,随时运行) ,所以是可以的
4.当出现了属性值,中含有对象怎么办?那么用这个就会无限循环,所以我们可以加这一行代码
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : {};
arguments.callee(prop, obj[i]);
} else {
obj[i] = prop;
}
}
return obj;
}
其实还有一个很好的方法
我们去想一下为什么我们不可以把这个对象直接赋值,因为他是引用类型,不是基本类型,那么其实我们可以先把他整体变成基本类型去赋值就可以了,最后在转成为引用类型就可以了
var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);
// 'function'
console.log(typeof obj2.fun);
// 'undefined' <-- 没复制
但是这个方法有2点不好
1.就是要用json的格式,属性值不可以用function 但是可以把他赋值去了
2.譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
第三种
继承的方法(如果不了解继承可以跳过)
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
if (prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
if(obj[i].constructor === Array){
console.log('数组');
arguments.callee(prop, obj[i]);
}
} else {
obj[i] = prop;
}
}
return obj;
}
Object.create(prop);的作用是什么?
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 ---MDN
所以作用就继承了上一层的值,也达到了效果obj[i]继承了prop的属性值,当他去改变的时候,只是新增加他自己的属性值,而
他继承的属性,的属性值他是改变不了的,所以也是带到了效果
第4种
用jquery 和 lodash 的函数框架
好了这个内容就到这里了 如果文中有什么错误,可以来和我说一下,嘿嘿