值类型与引用类型
值类型
简单来说,值类型就是将一个变量赋值给另一个变量后,两个变量完全独立,改变其中的一个并不会影响另一个。
var a = 1;
var b = a; // b = 1
a = 2; // a = 2 b = 1
像上面的例子中,虽然后声明的变量b赋予了a的值,但是改变a的值,b却没有改变
除了数值类型,与此类似的 js 中的值类型还有布尔值、字符串、null、undefined等
引用类型
引用类型刚好与值类型相反,原始的变量被改变后,被赋值的变量也会被改变
引用类型会在内存中开辟一块区域保存它的值,而被赋予了这个值的原始变量本质上是将指向了这块内存,而被赋值的另一个变量获得的也只是这个指向而已
所以一旦内存上的值改变,所有只想这块内存的变量的值都会被改变
var c = [1,2,3];
var d = c; // d = [1,2,3]
c[0] = 0; // c = [0,2,3] d = [0,2,3]
js 中常见引用类型有 数组、对象、函数
对象的浅拷贝
对象的浅拷贝简单,就是将一个变量赋给另一个变量
var obj1 = {
name: 'test name',
age: 18
}
var obj2 = obj1;
上面的例子中 obj2 经过浅拷贝拥有了 obj1 的属性
浅拷贝存在的问题
我们知道引用类型的赋值其实是改变了变量的指向,那么如果在需要拷贝的对象中存在某个属性的值是引用类型,如数组或子对象,那么浅拷贝后的原对象的属性获得的也只是这个指向
所以如果改变被拷贝对象的属性值,那么原对象的相应属性也会跟着改变
对象的深拷贝
深拷贝其实就是将对象中的数组、子对象进行深度递归遍历,直到其不是引用类型位置再进行复制,这样即使改变了其中一个的值,也不会影响到另一个
JSON.parse()与 JSON.stringify()
var target = JSON.parse(JSON.stringify(source2))
target['0'] = 5
target['3'].name = 'anpoly'
target['4'][0] = 6
console.log(source2) // {0: 1, 1: null, 2: undefined, 3: {name: "anjou", age: undefined, sex: null}, 4: [2, 3, 4], 5:
ƒ ()}
console.log(target) // {0: 1, 1: null, 3: {name: "anpoly", sex: null}, 4: [6, 3, 4]}
可以实现对象的深度克隆,但是一些不安全的JSON值(undefined 、 function 和 symbol)无法拷贝。这是因为JSON.stringify(…) 在对象中遇到 undefined 、 function 和 symbol 时会自动将其忽略, 在 数组中则会返回 null (以保证单元位置不变)
克隆函数
深拷贝的缺点
虽然深拷贝能够避免浅拷贝出现的问题,但是却会带来性能上的问题,如果一个对象非常复杂或数据庞大,所消耗的性能将会是很可观的。
// 克隆函数
function deepCopy(data) {
if (typeof data !== 'object' || data == null) return data
var newData = data instanceof Array ? [] : {}
for (var key in data) {
newData[key] = typeof data[key] === 'object' ? (data[key] === null ? null : deepCopy(data[key])) : data[key]
}
return newData
}
var target = deepCopy(source)
target[0] = 5
target[3].name = 'anpoly'
target[4][0] = 6
console.log(source) // [1, null, undefined, {name: 'anjou', age: undefined, sex: null}, [2, 3, 4], ƒ ()]
console.log(target) // [5, null, undefined, {name: 'anpoly', age: undefined, sex: null}, [6, 3, 4], ƒ ()]
递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。