再来聊聊深浅拷贝~🐟 🐟
再来聊一聊深浅拷贝~🐟 🐟
我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第3篇文章 点击查看文章详情 🚀🚀
前言
深浅拷贝是面试常常出现的考题,虽然自己也能说出一些拷贝的api,但总是满足不了面试官的要求,所以打算在此基础上再深挖一下。也希望对深浅拷贝更加了解后,能够加深印象。不至于看了忘,忘了看。😕 😕
定义
先来理一遍深浅拷贝的定义
浅拷贝
创建一个新对象
,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型
,拷贝的就是基本类型的值
,如果属性是引用类型
,拷贝的就是内存地址
,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝
将一个对象从内存中完整的拷贝一份出来,从
堆内存
中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
不理解堆栈内存?没事,接着往下看~ 👇
基本数据类型存储特点:数据直接存储在栈中。
引用数据类型存储特点:存储在栈(stack)中的是对象的引用地址,而真实的数据存放在堆(heap)内存中。
那么接下来先看一个最基本的问题
赋值和浅拷贝的区别
相信有不少初学者和我一样,刚开始以为赋值和浅拷贝是一样的,或者知道它们不一样,但却又不能说出个所以然~ 😕
-
先看赋值,将一个对象赋值给一个新的对象的时候,赋的其实是该对象在
栈中的地址
,而不是堆中的数据。也就是一个对象的改变就会改变另外一个对象
。 -
再看浅拷贝,浅拷贝会
创建一个对象
,再去遍历原始对象
,如果原对象的属性值是基础类型,那么就拷贝基础类型,如果是引用类型,则拷贝的是指针。
只谈定义,不给例子的都是在耍流氓
🙅🙅
- 赋值
var obj1 = {
name: '前端大杂货铺',
content: ['前端', '生活']
}
// 赋值
var obj2 = obj1
obj2.name = 'Gping'
obj2.content[2] = '感悟'
console.log(obj1, obj2)
就是因为将obj1赋值给obj2时,传递的是obj1的地址。也就是一个对象的改变就会改变另外一个对象
- 浅拷贝
var obj1 = {
name: '前端大杂货铺',
content: ['前端', '生活']
}
// Object.assign 是浅拷贝
var obj3 = Object.assign({}, obj1)
obj3.name = 'Gping'
obj3.content[2] = '感悟'
console.log(obj1, obj3)
浅拷贝会创建一个对象,再去遍历原始对象。后面再进一步对原始对象中的简单数据类型和引用数据类型进行分类。
总结
:赋值和浅拷贝的区别在于对象第一层数据对原对象的影响,如果是赋值,改变会直接影响原对象。如果是浅拷贝,而且属性值是基础类型的话,就不会影响原对象。属性值为引用类型,就会影响原对象。
浅拷贝
废话不多说,直接上API! 🚀🚀🚀
Object.assign()
:
可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象
```
const obj1 = { name: 'dog', info: { age: 3 } }
const obj2 = Object.assign({}, obj1)
```
函数库lodash的.clone方法
```
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.clone(obj1);
console.log(obj1.b.f === obj2.b.f);// true
```
展开运算符
```
const obj1 = { name: 'dog', info: { age: 3 } }
const obj2 = { ...obj1 }
obj2.name = 'cat'
obj2.info.age = 4
console.log(obj1) // { name: 'dog', info: { age: 4 } }
console.log(obj2) // { name: 'cat', info: { age: 4 } }
```
> 注意: 当拷贝对象只有一层的时候,是深拷贝
Array.slice()
```
let a = [1,2,3,4]
let b = a.slice();
a.push(5);
console.log(a); // -> [1, 2, 3, 4, 5]
console.log(b); // -> [1, 2, 3, 4]
```
手写浅拷贝
很多时候,只知道api也满足不了面试官,我们也得掌握手写。🤔
function clone(target) {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = target[key];
}
return cloneTarget;
};
创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性依次添加到新对象上,返回。
深拷贝
JSON.parse(JSON.stringify())
例子
const obj1 = {
name: 'dog',
info: { age: 3 },
fn: function () {}
}
const obj2 = JSON.parse(JSON.stringify(obj1))
obj2.name = 'cat'
obj2.info.age = 4
console.log(obj1)
// { name: 'dog', info: { age: 3 }, fn: function(){} }
console.log(obj2)
// { name: 'cat', info: { age: 4 } }
利用
JSON.stringify
将对象转成JSON字符串,再用JSON.parse
把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝
这里也可以很明显的看到JSON.parse(JSON.stringify())的不足,它并不能拷贝函数
手写深拷贝
对于深拷贝,我们要拷贝的对象是不知道有多少层深度的,我们可以用递归来解决问题。
- 如果是原始类型,无需继续拷贝,直接返回
- 如果是引用类型,创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性执行深拷贝后依次添加到新对象上。
很容易理解,如果有更深层次的对象可以继续递归直到属性为原始类型,这样我们就完成了一个最简单的深拷贝
:
function clone(target) {
if (typeof target === 'object') {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
};
考虑数组
module.exports = function clone(target) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
};
参考文档
结语
这就是这次的全部内容了~
希望能够对屏幕前的朋友们有一丝丝帮助吧!
当然这篇文章也有许多不足的地方,欢迎大佬们积极指出错误,Thanks♪(・ω・)ノ
❤️❤️
转载自:https://juejin.cn/post/7144371259034304548