写点什么

JavaScript 深拷贝与浅拷贝

用户头像
好编程
关注
发布于: 2020 年 09 月 05 日

一、深拷贝

首先思考问题

  • 深拷贝是什么?

  • 如何实现深拷贝?

  • 它的使用场景是什么?



深拷贝是什么?

值类型与引用类型的复制,复制值类型给另一个变量时,他们的值是独立的,互不干扰的。复制引用类型给另一个对象时,复制的是该对象的引用地址,导致他们会指向同一个地址,同一份数据。解决办法是深拷贝。



如何实现深拷贝?

我们刚刚知道了复制值类型时是复制的值是独立的,我们利用这个特点,使用递归的方式进行对一个对象进行深拷贝。



封装一个公共方法 - 判断一个值是否为对象:

/*
* 判断是否为对象
*/
function isObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}



使用递归的方法实现深拷贝:

/**
* 深拷贝
*/
function deepClone(source) {
if (!isObject(source)) {
return source;
}
let target = Array.isArray(source) ? [] : {};
for (let k in source) {
if (source.hasOwnProperty(k)) {
// 判断子属性为对象时,继续递归
if (typeof source[k] === "object") {
target[k] = deepClone(source[k]);
} else {
target[k] = source[k];
}
}
}
return target;
}



该实现方式可以解决大部分的深拷贝需求,但有一个缺点:当拷贝的对象层次非常多的时候,递归会爆栈溢出。解决方法:利用循环替代递归:

/**
* 深拷贝 -- 解决递归爆栈
*/
function cloneLoop(source) {
if (!isObject(source)) {
return source;
}
const root = {};
const loopList = [
{
parent: root,
key: undefined,
data: source,
},
];
while (loopList.length) {
const node = loopList.pop();
const data = node.data;
const parent = node.parent;
const key = node.key;
// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
let target = parent;
if (typeof key !== "undefined") {
target = parent[key] = {};
}
for (let k in data) {
if (data.hasOwnProperty(k)) {
if (typeof data[k] === "object") {
loopList.push({
parent: target,
key: k,
data: data[k],
});
} else {
target[k] = data[k];
}
}
}
}
return root;
}



还有一个非常简单的深拷贝方法:

// 深拷贝
JSON.parse(JSON.stringify(source));



这个方法可以满足基本开发深拷贝的需求,但它存在缺点:

  1. 无法拷贝源对象的函数,不能序列化函数

  2. 无法拷贝源对象原型链上的属性与方法

  3. 会忽略 undefined 会忽略 symbol

  4. 不能解决循环引用的对象

  5. 层次太深,会爆栈

使用场景

深拷贝是解决能够独立复制引用类型的问题。在开发中,这类问题稍微不留意就会出问题。大家在开发中遇到问题建议先静下来,慢慢 debugger 看下哪里出了问题,再找出解决问题的方案。



二、浅拷贝

了解了深拷贝,理解浅拷贝也简单了,浅拷贝的话,复制的其实也是同份引用地址。我们使用 JS 里面的几个特性和方法可以实现浅拷贝:

  • Object.assign()

  • ES6 展开运算符 ...



拷贝对象

const obj = {
a: {
b: {
c: "bobo",
},
},
};

浅拷贝方法

// Object.assign() 方法
const o = Object.assign({}, obj); // 直接拷贝第一层
// 比如修改 obj 对象下的 a 对象下的 b 对象
obj.a.b = { name: "js" };
console.log(obj); // {a: {b: {name: 'js'}}}
console.log(o); // {a: {b: {name: 'js'}}} o 也跟着 obj 改变了
// 展开运算符
const o = {...obj}
const arr = [...array]



实现浅拷贝的方法

/*
* 实现浅拷贝
*
* 实际上是只做一层的赋值操作
*/
function shallowClone(source) {
if (!isObject(source)) {
return source;
}
const target = {};
for (let k in source) {
if (source.hasOwnProperty(k)) {
target[k] = source[k];
}
}
return target;
}



发布于: 2020 年 09 月 05 日阅读数: 79
用户头像

好编程

关注

您好,编程! 2019.01.10 加入

Haocode.com

评论

发布
暂无评论
JavaScript 深拷贝与浅拷贝