数据类型
说到深浅拷贝就不得不说到 js 的数据类型,在 js 中有两大数据类型基本数据类型,引用数据类型。 基本数据类型是存储在栈内存中,引用数据类型存储在堆内存中,引用数据类型的变量是一个指向堆内存的的引用,是一个地址值,存在栈中。
浅拷贝
浅拷贝是指创建一份跟原始数据值相同的复制品;规则额是这样的,如果是基本数据类型就拷贝基本类型的值,如果是引用数据类型就拷贝内存地址。也可以这么说浅拷贝就是拷贝一层,深层次的引用类型数据就共享内存地址。 用原生 js 实现一个浅拷贝如下:
js
function shallowClone(obj) {
const shallowObj = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
shallowObj[key] = obj[key];
}
}
return shallowObj;
}
在 js 中原生提供了一些方法供我们实现浅拷贝
- Object.assign()
- Array.prototype.slice()
- Array.prototype.concat()
- es6 提供的拓展运算符
看一些例子
Object.assign()
js
const obj = {
name: "zhangsan",
age: 18,
hobby: ["apple", "banana"],
address: {
province: "省",
city: "市",
area: "区",
},
say: function () {
console.log("hello");
},
};
const newObj = Object.assign({}, obj);
Array.prototype.slice()
js
const arr = [1,2,3],
const newArr = arr.slice(0)
newArr[0] = 2
// arr [1,2,3]
// newArr [2,2,3]
Array.prototype.concat()
js
const arr = [1, 2, 3];
const newArr = arr.concat();
newArr[0] = 2;
// arr [1,2,3]
// newArr [2,2,3]
ES6 扩展运算符
js
const arr = [1, 2, 3];
const newArr = [...arr];
newArr[0] = 2;
// arr [1,2,3]
// newArr [2,2,3]
// 对象同理
const obj = { a: 1, b: 2 };
const newObj = { ...obj };
newObj[a] = 2;
// obj { a: 1, b: 2 }
// newObj { a: 2, b: 2 }
深拷贝
对于基本数据类型来说深拷贝浅拷贝都是一样的,对于引用数据类型来说,深拷贝就是新开一块内存空间,存放的是一份和原始值一样的数据但是内存地址是不同的,所以修改新对象的属性并不会改变原始对象的属性。
常用的深拷贝方法
- lodash 中的 cloneDeep
- JSON.stringify() 该方式有很大弊端,当遇到函数、undefined、symbol 时会无法复制,且无法解决循环引用
- js 原生手写递归深拷贝函数
原生实现一个深拷贝函数
js
function cloneDeep(obj, hash = new WeakMap()) {
// 如果是null直接return
if (obj === null) return obj;
// Date
if (obj instanceof Date) return new Date(obj);
// Reg
if (obj instanceof RegExp) return new RegExp(obj);
// 如果是基本属性类型或者函数直接return
if (typeof obj !== "object") return obj;
// 如果是对象且缓存中有值,直接从缓存中取值
if (hash.get(obj)) return hash.get(obj);
// 以obj原型的constructor创建一个新对象,因为它指向当前类本身
const cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
// 递归
cloneObj[key] = cloneDeep(obj[key], hash);
}
}
return cloneObj;
}