对以前写过的博客进行补充和回顾 https://www.jianshu.com/p/9c818497f25f
一、基本数据类型
Number String Boolean Undefined Null Symbol(ES6)
基本数据类型存放于栈内存。包括变量标识符和变量的值
let a = 1;
let b = 2;
基本类型赋值后两个变量互不影响
let a = 10;
let b = a;
二、引用数据类型
引用类型的存储需要内存的栈区和堆区共同完成,栈区内存保存变量标识符和指向堆内存中该对象的指针,也可以说是该对象在堆内存的地址,而堆内存中存储这个对象
let person1 = {name:'张三'};
let person2 = {name:'张三'};
let person3 = person1;
引用类型的赋值是地址的复制,所以两个变量指向的还是同一个对象,对任何一个的操作都会相互的影响
person3.name = '李四'
console.log(person1.name) // 李四
三、使用深度克隆的几种方式
1. Object.assign的方式
function cloneDeep(obj) {
return Object.assign({}, obj);
}
MDN:Object.assign讲解
弊端:
-
假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。这时候对于嵌套的对象深度拷贝是无效的。例如下面的——
let oldObj = { name: '张三', friend: { name: '李四' } };
2. JSON.parse()和JSON.stringify()的方式
function cloneDeep(obj) {
return JSON.parse(JSON.stringify(obj));
}
这种方式实现通过
JSON.stringify()
将对象或数组进行JSON字符串化,这时候他就是一个基本数据类型,然后在通过JSON.parse
进行解析。
MDN:JSON.stringify()讲解
MDN:JSON.parse()讲解
弊端:
- 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象;
- 如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;
- 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
- 如果obj里有NaN、Infinity和 - Infinity,则序列化的结果会变成null
3. 相对全面的一个方式
function cloneDeep(obj) {
if (typeof obj !== 'object') {
return obj;
}
if (!obj) { // obj 是 null的情况
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
if (obj instanceof Function) {
return obj;
}
// 定义一个新的空数组或者对象
let newObj = Array.isArray(obj) ? [] : {}
if (obj && typeof obj === "object") {
for (let key in obj) {
// 执行 hasOwnProperty() 来确定某属性是否是对象本身的属性
if (obj.hasOwnProperty(key)) {
newObj[key] = (obj && typeof obj[key] === 'object') ? cloneDeep(obj[key]) : obj[key];
}
}
}
return newObj;
}
弊端:
- 较为复杂
4. 直接使用lodash中的方法
项目中使用最好使用他人已经分装好的库来使用,比我们考虑的更为周全和简洁。这里推荐
lodash
,lodash 是一个 JavaScript 的实用工具库,众多常用的方法他都给我们封装好了
点击查看lodash文档
下面一个官网栗子
直接引用库,然后调用其方法即可
const _ = require('lodash')
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false