JavaScript的数据类型有:
基本数据类型: Number、String、Boolean、null、undefined (栈内存)
引用数据类型: Object (堆内存)
保存在栈内存还是堆内存的根本原因是什么?
根本原因在于保存在栈内存的必须是大小固定的数据,引用类型的大小不固定,只能保存在堆内存中,但是可以把它的地址写在栈内存中以供我们访问
如果是基本数据类型,则按值访问,操作的就是变量保存的值;如果是引用类型的值,我们只是通过保存在变量中的引用类型的地址来操作实际对象
基本类型的复制
var a = 1;
var b = a;//复制
console.log(b)//1
a = 2;//改变a的值
console.log(b)//1
基本类型在复制时,相当于在栈内存中重新开辟了一个内存,存放变量b,因此在修改a时,b和a互不影响
引用类型的复制
var color1 = ['red','green'];
var color2 = color1;//复制
console.log(color2)//['red','green'];
color1.push('black') ;//改变color1的值
console.log(color2)//['red','green','black']
引用类型在复制时,相当于复制的是某个对象的指针(即引用地址),而不复制对象本身。新旧对象还是共享同一块内存,因此当color1更改了之后,color2也会跟着改变。
针对引用类型的拷贝
由上述可知,引用类型复制的是指针,那么如何切断两者之间的关系呢?可以拷贝一份它的数据,根据拷贝的层级又分浅拷贝和深拷贝。
浅拷贝:
- 浅拷贝只拷贝一层
// 浅拷贝
function shallowClone(source) {
var target = {}
for (var i in source) {
if (source.hasOwnProperty(i)) {
target[i] = source[i]
}
}
return target
}
var a1 = {b: {c: {d: 1}}}
var a2 = shallowClone(a1)
a1.b.c = 4
console.log(a1) // { b: { c: 4 } }
console.log(a2) // { b: { c: 4 } }
- ES6 的 Object.assign()
- ES7 的...解构运算符
深拷贝:深拷贝就是能够实现真正意义上的数组和对象的拷贝。递归调用"浅拷贝"。(深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象)
深拷贝的方式:
- JSON.parse(JSON.stringify(src)):这种方法有局限性,如果属性值是函数或者一个类的实例的时候,无法正确拷贝
const liLei = {
name: 'lilei',
age: 28,
habits: ['coding', 'hiking', 'running']
}
const liLeiStr = JSON.stringify(liLei)
const liLeiCopy = JSON.parse(liLeiStr)
liLeiCopy.habits.splice(0, 1)
console.log('李雷副本的habits数组是', liLeiCopy.habits)
console.log('李雷的habits数组是', liLei.habits)
- 采用递归的方式
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) { // 基本类型本身就是深拷贝
return obj
}
// 定义结果对象
let copy = {}
// 定义结果数组
if (obj.constructor === Array) {
copy = []
}
// 遍历对象的Key
for (let key in obj) {
console.log('key', key)
// 如果key是对象的自有属性
if(obj.hasOwnProperty(key)) {
// 递归调用深拷贝方法
copy[key] = deepClone(obj[key])
}
}
return copy
}
const obj = {
a: 1,
b: 2,
c: 3
}
var a1 = {b: {c: {d: 1}}}
var a2 = deepClone(a1)
a1.b.c = 4
console.log(a1) // { b: { c: 4 } }
console.log(a2) // { b: { c: { d: 1 } } }
采用递归的方式调用深拷贝方法,若属性为值类型,则直接返回;若属性为引用类型,则递归遍历。
-
借助第三方库
-
lodash 的cloneDeep(src)
-
jq 的extend(true, result, src1, src2[ ,src3])
-