一、赋值
赋值是将某一数值或对象赋给某个变量的过程
- 基本数据类型:赋值,赋值之后两个变量互不影响
- 引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有影响
对基本数据类型赋值操作,两个变量互不影响
var a = 'Mike';
var b = a;
console.log(b);//Mike
a = "Lily";
console.log(a);//Lily
console.log(b);//Mike复制代码
对引用数据类型赋址操作,两个变量指向同一个对象,改变a就会影响b
var a = {
name : 'Mike',
like : ['js','css']
}
var b = a;
console.log(b);
//{
// name:'Mike',
// like:['js',css]
//}
a.age = 17;
console.log(b);
//{
// name:'Mike',
// like:['js',css],
// age : 17
//}复制代码
通常在开发中并不希望改变变量a会影响到变量b,这时候就需要深拷贝和浅拷贝。
二、浅拷贝
1、什么是浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的是基本类型值,如果是引用类型,拷贝的是内存地址,所以如果其中一个对象改变了地址,就会影响另一个对象
简单来说,可以理解为浅拷贝只解决了第一层的问题
2、浅拷贝的使用场景
- Object.assign()
- 展开运算符
- Array.prototype.slice()
var a = {
name : 'Mike',
like : [
{
title:'js',
bookName:'高级程序设计'
},
{
title:'css',
bookName:'css世界'
},
]
};
var b = Object.assign({},a);
console.log(b);
//{
// name:'Mike',
// like:[
// {
// title:'js',
// bookName:'高级程序设计'
// },
// {
// title:'css',
// bookName:'css世界'
// }
// ]
//}
a.name = 'Lily';
a.like[0].title= 'html5';
a.like[0].bookName= 'html5编程';
console.log(b);
//{
// name:'Mike',
// like:[
// {
// title:'html5',
// bookName:'html5编程'
// },
// {
// title:'css',
// bookName:'css世界'
// }
// ]
//}
复制代码
上面代码说明改变a的name属性值(基本类型)时,没有影响到b的name属性值,而当改变a的like属性值(引用类型)时,b的like属性值相应的发生变化。
展开运算符
var a = {
name : 'Mike',
like : [
{
title:'js',
bookName:'高级程序设计'
},
{
title:'css',
bookName:'css世界'
},
]
};
var b = {...a};
console.log(b);
//{
// name:'Mike',
// like:[
// {
// title:'js',
// bookName:'高级程序设计'
// },
// {
// title:'css',
// bookName:'css世界'
// }
// ]
//}
a.name = 'Lily';
a.like[0].title= 'html5';
a.like[0].bookName= 'html5编程';
console.log(b);
//{
// name:'Mike',
// like:[
// {
// title:'html5',
// bookName:'html5编程'
// },
// {
// title:'css',
// bookName:'css世界'
// }
// ]
//}复制代码
展开运算符和Object.assign()的实际效果一样,只能拷贝第一层
Array.prototype.slice()
slice()方法返回一个新数组对象,arr.slice([begin[, end]])
var a = [0,1,[3,4]];
var b = a.slice();
console.log(b);//[0,1,[3,4]]
a[2][0] = 5;
console.log(b);//[0,1,[5,4]]复制代码
当改变a[2][0]时,b也相应发生变化。
三、深拷贝
1、什么是深拷贝
深拷贝会拷贝所有属性,并拷贝属性指向的动态分配内存。拷贝前后两个对象互不影响。
2、深拷贝的使用场景
- JSON.parse(JSON.stringify(object))
- jQuery.extend()
- lodash.cloneDeep()
var a = {
name : 'Mike',
like : [
{
title:'js',
bookName:'高级程序设计'
},
{
title:'css',
bookName:'css世界'
},
]
};
var b = JSON.parse(JSON.stringify(a));
a.name = 'Lily';
a.like[0].title= 'html5';
a.like[0].bookName= 'html5编程';
console.log(b);
//{
// name:'Mike',
// like:[
// {
// title:'js',
// bookName:'高级程序设计'
// },
// {
// title:'css',
// bookName:'css世界'
// }
// ]
//}
console.log(a);
//{
// name:'Lily',
// like:[
// {
// title:'html5',
// bookName:'html5编程'
// },
// {
// title:'css',
// bookName:'css世界'
// }
// ]
//}
复制代码
var a = [0,1,[3,4]];
var b = JSON.parse(JSON.stringify(a));
a[2][0] = 5;
console.log(a);//[0,1,[5,4]]
console.log(b);//[0,1,[3,4]]复制代码
以上两段代码说明,通过使用JSON.parse(JSON.stringify(object)),可以实现对数组和对象的深拷贝。
但该方法有以下几个问题:
1、会忽略undefined
2、会忽略symbol
3、不能序列化函数
4、不能处理正则
5、不能解决循环引用的对象
- 会忽略undefined 、会忽略symbol、不能序列化函数、不能处理正则
var obj = {
a:undefined,//undefined
b:Symbol('mike'),//Symbol()
c:function(){},//函数
e:/'123'/
};
var b = JSON.parse(JSON.stringify(obj));
console.log(obj);
//{
// a:undefined,
// b:Symbol('mike'),
// c:function(){},
// e:/'123'/
//}
console.log(b);
//{ e:{}}复制代码
- 循环引用情况下会报错
var obj = {
a:1,
b:{
c:2
}
};
obj.a = obj.b;
obj.b.c = obj.a;
let b = JSON.parse(JSON.stringify(obj));
console.log(b);//VM288:13 Uncaught TypeError: Converting circular structure to JSON(…)复制代码
以上问题,和JSON有关。
四、如何实现深拷贝
function deep(source,hash = new WeakMap()){
//通过使用hash来解决循环引用的问题
if(hash.has(source)) return hash.get(source);
//判断是对象还是数组
var target = Array.isArray(source) ? [] : {};
hash.set(source,target);
for(var key in source){
//判断是否是自身属性,不是继承属性
if(Object.prototype.hasOwnProperty.call(source,key)){
//判断属性值是否是引用类型,如果是引用类型则递归继续执行该函数
if(typeof source[key] === 'object' && source[key] !== null){
target[key] = deep(source[key],hash)
}else{
target[key] = source[key];
}
}
}
return target;
}复制代码