浅拷贝和深拷贝:
针对对象
深拷贝和浅拷贝只针对像Object和Array这样的复杂数据类型的,String,Number等简单类型不存在深拷贝。
浅拷贝:
因为浅拷贝只会将对象的各个属性进行依次复制,并不会进行递归复制。在JavaScript中,对于Object和Array这类引用类型值,当从一个变量向另一个变量复制引用类型值时,这个值的副本其实是一个指针,两个变量指向同一个堆对象,改变其中一个变量,另一个也会受到影响。所以浅拷贝会导致 obj.arr 和 shallowObj.arr 指向同一块内存地址,当修改obj.arr的值时,shallowObj.arr的值同样会被修改,
eg:
var a={a:1,b:2,c:3}
var b=a
b.a=2
console.log(a,b)
深拷贝
而深拷贝则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深拷贝的方法递归复制到新对象上。这就不会存在上面 obj 和 shallowObj 的 arr 属性指向同一个对象的问题。当修改obj.arr的值时,shallowObj.arr的值不会被修改,仍然为原值
eg:
function deepClone(obj){
let obj_ = JSON.stringify(obj),
loneObj = JSON.parse(obj_);
return loneObj
}
let a=[1,2,3,4],
b=deepClone(a);
b[0] = 2;
console.log(a,b);
弊端:
- 如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
eg:
var test = {
name: 'a',
date: [new Date(1536627600000), new Date(1540047600000)],
};
let b;
b = JSON.parse(JSON.stringify(test));
console.log(b);
- 如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
eg:
const test = {
name: 'a',
date: new RegExp('\\w+'),
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
//console.error('ddd', test, copyed);
console.log(copyed);
- 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
eg:
const test = {
name: 'a',
date: function hehe() {
console.log('fff')
},
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed);
-
如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
-
JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;
eg:
function Person(name) {
this.name = name;
console.log(name)
}
const liai = new Person('liai');
const test = {
name: 'a',
date: liai,
};
// debugger
const copyed = JSON.parse(JSON.stringify(test));
test.name = 'test'
console.error('ddd', test, copyed)
- 如果对象中存在循环引用的情况也无法正确实现深拷贝;
总结:
用法简单,然而使用这种方法会有一些隐藏的坑:因为在序列化JavaScript对象时,所有函数和原型成员会被有意忽略。
通俗点说,JSON.parse(JSON.stringfy(X)),其中X只能是Number, String, Boolean, Array, 扁平对象,即那些能够被 JSON 直接表示的数据结构。
JSON.stringify()的几种妙用:
判断数组是否包含某对象,或者判断对象是否相等。
let data = [
{name:'echo'},
{name:'听风是风'},
{name:'天子笑'},
],
val = {name:'天子笑'};
console.log(JSON.stringify(data).indexOf(JSON.stringify(val)) !== -1);
console.log(data.indexOf(val) !== -1)
//因为数组和对象是引用值不能比较,所以需要序列化一下转成字符串
判断两数组/对象是否相等
let a = [1,2,3],
b = [1,2,3];
JSON.stringify(a) === JSON.stringify(b);//true
使用localStorage/sessionStorage可以存储对象。
localStorage/sessionStorage默认只能存储字符串,而实际开发中,我们往往需要存储的数据多为对象类型,那么这里我们就可以在存储时利用json.stringify()将对象转为字符串,而在取缓存时,只需配合json.parse()转回对象即可。
//存
function setLocalStorage(key,val){
window.localStorage.setItem(key,JSON.stringify(val));
};
//取
function getLocalStorage(key){
let val = JSON.parse(window.localStorage.getItem(key));
return val;
};
//测试
setLocalStorage('demo',[1,2,3]);
let a = getLocalStorage('demo');//[1,2,3]
JSON.stringify()与toString()这两者虽然都可以将目标值转为字符串,但本质上还是有区别的,比如
let arr = [1,2,3];
console.log(JSON.stringify(arr));//'[1,2,3]'
console.log(arr.toString());//1,2,3