浅拷贝与深拷贝
在聊浅拷贝与深拷贝之前,我们先来聊一聊JavaScript中的数据类型,以及栈和堆。 首先我们先来看个例子
// 例1
let a = 1
let b = a
b = 2
console.log(a) // 1
console.log(b) // 2
// 例2
let c = { name: 'simple', age: 25}
let d = c
d.name = 'Mr.simple'
console.log(c) // { name: 'Mr.simple', age: 25}
console.log(d) // { name: 'Mr.simple', age: 25}
例1中很符合我们所想的,修改变量b不会影响到变量a, 很符合常规逻辑,但是例2中,为什么我修改了变量d,变量c也受到影响了呢?我们慢慢来解释。
JavaScript数据类型
首先JavaScript是一门动态、弱类型语言, 我们在编写代码的时候不需要指定变量的类型, JavaScript编译器会自动推断。目前,JavaScript总共有7种基本类型和一种引用类型。如下展示。
Boolean 只有true和false两个值
Null 只有一个值null
Undefined 一个没有被赋值的变量会有个默认值undefined,变量提升的时的默认值也是undefined
Number JavaScript中的数字类型,基于IEEE75标准的双精度64位二进制格式的值(-2^63 - 1 到 2^63 - 1)
BigInt JavaScript中一个新的数字类型,可以用任意精度表示。使用BigInt,即使超出Number的安全范围限制, 也可以安全的存储和操作
String 字符串
Symbol 符号类型是唯一的并且是不可修改的,通常用来作为Object的key
Object 对象类型,被看做是一组属性的集合
之所以把他们区分为两种不同的类型,是因为他们在内存中存储的位置不同。 基本类型存储在栈中, 引用类型存储在堆中。
为什么会有栈和堆之分
当JavaScript执行的时候,会为每个函数(包括全局)生成一个执行上下文,大致结构如下。然后将执行上下文放入函数调用栈中,此时,会有一个栈针指向最新放入函数调用栈的执行上下文,当该函数执行完成以后,栈针向下移,此时,该执行上下文所占用的栈区就会被释放,当有新的执行上下文压入栈时,会直接覆盖,栈针上移。如此频繁的操作,当然是占用的内存越少,操作越快。
变量环境和词法环境会存储函数中的变量,当代码执行的时候,遇到基本类型,会把基本类型放置在变量环境或词法环境中,当遇到引用类型的时候,会在堆内存中开辟一块空间,将变量放进去,并且把堆内存中的地址存储在变量环境或词法环境中。
最终目的是让js执行速度加快,如此区分也有利于js的垃圾回收。
先来解释一下例题
当我们执行如上代码时, 代码存放方式如下
从上图可以看出,当执b = a
时, 会在栈内存中分配一个和a一模一样的地址用来存储b, 此时a和b相互独立,当修改b,a不会跟着变化
我们看例2的内存分配图, 栈中的变量c只存储着堆中对象的引用,当执行d = c
时,此时,变量d也只是复制了对象的引用,即c和d指向同一个对象,所有当修改d指向的对象时,c也会跟着变化。
浅拷贝
浅拷贝只是拷贝了对象的第一层属性, 所以可能还会与源对象有关联
浅拷贝实现
// 方法一
function shallowClone(target) {
let obj = Array.isArray(target) ? [] : {}
for(let key in target) {
obj[key] = target[key]
}
return obj
}
// 方法二
Object.assign()
let a = { name: 'simple', friend: { name: 'xiaoming', age: 23 } }
const b = shallowClone(a)
示意图如下
所以会导致如下情况,修改b.friend.name会间接修改a.friend.name
b.friend.name = 'xiaohong'
console.log(a.friend.name) // xiaohong
深拷贝
深拷贝是递归拷贝对象的所有属性,拷贝完成以后两个对象没有任何关系
function deepClone(target) {
const cache = [] // cache的作用是缓存复制过的属性,防止引用自身的对象造成递归无限循环,栈溢出
cache.push(target)
let dest = Array.isArray(target) ? [] : {}
for(let item in target) {
if(typeof target[item] === 'object') {
const index = cache.indexOf(target[item])
if(index > -1) {
dest[item] = cache[index]
} else {
dest[item] = deepClone(target[item])
}
} else {
dest[item] = target[item]
}
}
return dest
}
let a = { name: 'simple', friend: { name: 'xiaoming', age: 23 } }
const b = deepClone(a)
示意图如下
所以当修改b.friend.name的时候不会影响到a.friend.name
b.friend.name = 'xiaohong'
console.log(a.friend.name) // xiaoming
console.log(b.friend.name) // xiaohong
以上,就是本人对JavaScript浅拷贝和深拷贝的理解,比较浅显 ^.^