01
数据类型介绍
JavaScript中的变量一共有两种类型的值: 基本类型值 和 引用类型值。
- 基本类型值:是指
String
、Number
、undefined
、Null
、boolean
等,他们在内存中都是存储在栈中的 , 即直接访问该变量就可以得到存储在栈中的该变量的值。若将一个变量的值赋值给另一个变量, 则这两个变量在内存中是独立的, 修改其中一个变量的值, 不会影响另一个变量。
我们来看一下图片介绍:
1. 创建了第一个变量 a , 并赋值一个 ’ 哈哈 ’ 给它,则在内存中的表现 形势为:
2. 然后又创建了一个变量 b ,将 变量 a赋值给b,则此时在内存中的表现形 式为:
可以看到在栈中, b独立占有一个位置,并且拥有自己的值为 ’ 哈哈 ’
3. 然后此时我们将变量a 的值改为 ’ 嘿嘿 ’ , 此时在内存中的情况是:
可以看到, 只有变量a的值变为了 ’ 嘿嘿 ’ , 这是因为 b 已经独立存在于栈中了,他不会受到 变量a 的影响。
- 引用类型值:是指
Object
、Array
、Function
等,他们在内存中是存在于栈与堆中, 即我们要访问到引用类型值的话, 需要先访问到该变量在栈中的地址(指向堆中的值), 然后再通过这个地址,访问到存放在堆中的数据。
1. 我们先创建一个变量 a ,并赋值一个{name: '张三'}
,此时在内存中的 情况就是:
我们可以看到, 给变量a 赋值了一个 Object数据类型后,在内存中是,Object存储在堆中,而栈中存储的是该变量名和对应堆中的Object的地址
2. 然后我们再创建一个变量b,将变量a 赋值给变量b,此时内存中的情况 是:
我们可以看到, 当把变量a 赋值给变量b 的时候, 只是在栈中创建了一个变量b,然后将 变量a 存储在栈中的地址1
给了变量b , 所以此时变量a 和 变量b都指向堆中的同一个值.
3. 最后我们 改变一下变量a 中的值, 即a.name = ' 李四 '
, 此时内存中 的情况是:
我们可以看到,因为变量a 和 变量b 都指向堆中的同一个值,所以当通过变量a 改变该值时, 变量a 和 变量b 对应的值也都都会跟着变。
那么如何使变量b 独立存在而不受变量a 的影响呢?接下来我们看看浅拷贝和深拷贝
02
浅拷贝
首先说一下, 深拷贝和浅拷贝主要是针对像Object 和 Array 这两种比较复杂的对象的。
那么什么是浅拷贝呢?简单来说,就是一个变量赋值给另一个变量,其中一个变量的值改变,两个变量的值都变了,这就叫做浅拷贝。
我们来看一个最简单的浅拷贝例子:
let a = {name: '张三', age: 19, like: ['打篮球', '唱歌', '跳舞']}let b = {}for(let i in a) { b[i] = a[i]}a.name = '李四'a.like[0] = '睡觉'console.log(a)console.log(b)// { name: '李四', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }// { name: '张三', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }
我们可以看到,变量b 通过一个一个获取变量a 中的元素, 已经独立存在, 改变了变量a 的 name,变量b 的 name却没有跟着改变。但是又能发现,变量a 改变了 like 值, 变量b 也随之改变了,好像并没有独立存在。其实这是因为变量b 在获取变量a 的每一个元素时,遇到 like , 发现它是我们上边说到的引用类型值
, 所以变量b 就获得了一个地址,指向了堆中的值, 所以变量b 中的 like 仍然不是独立存在的。
这就是一个浅拷贝的例子。总结来说就是, 浅拷贝只是将外层的值独立了出来, 例如这个例子中的 name 、 age , 都不会随着变量a 的改变而改变了;但是由于对象结构复杂,内部的引用类型值没有独立出来, 例如这个例子中的 like , 变量b 获取到以后只是获取到了一个地址, 它跟变量a 的 like 指向同一个对象, 所以变量b 的 like 仍然会随着 变量a 的 like 改变而改变。
Object.assign
ES6语法中也给我们提供了一个浅拷贝的方法Object.assign(target, sources)
- target:拷贝的目标
- sources:被拷贝的对象
let a = {name: '张三', age: 19, like: ['打篮球', '唱歌', '跳舞']}let b = {}Object.assign(b, a)a.name = '李四'a.like[0] = '睡觉'console.log(a)console.log(b)// { name: '李四', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }// { name: '张三', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }
03
深拷贝
那么浅拷贝, 是拷贝后,新拷贝的对象内部仍然有一部分数据会随着源对象的变化而变化, 那么深拷贝就是,拷贝后, 新拷贝的对象内部所有数据都是独立存在的,不会随着源对象的改变而改变。
深拷贝的话一共有两种方式: 递归拷贝
和 利用JSON函数深拷贝
- 递归拷贝
实现原理:对变量中的每个元素进行获取,若遇到基本类型值
,直接获取;若遇到引用类型值
, 则继续对该值的内部每个元素进行获取。
这里就不多做演示了,因为用递归来实现深拷贝很少,简单运用的话,用的最多的就是第二种深拷贝方式了。
- 利用JSON函数深拷贝
实现原理:将变量的值转变成字符串形式, 然后再转化成对象赋值给新的变量
let a = {name: '张三', age: 19, like: ['打篮球', '唱歌', '跳舞']}let b = JSON.parse(JSON.stringify(a)) a.name = '李四'a.like[0] = '睡觉' console.log(a)console.log(b)// { name: '李四', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }// { name: '张三', age: 19, like: [ '打篮球', '唱歌', '跳舞' ] }
可以看到, 变量b 已经完全独立存在了, 无论变量a 怎么变, 变量b 都保持不变了。