深浅拷贝
数据类型及特点
- 基本数据类型
Undefined, Null, Symbol, Boolean, String, Number
特点:直接存储在栈中的数据 - 对象数据类型(引用数据类型)
特点:栈中存储的是对该对象的引用,真实的数据存储在堆内存中
引用数据类型在栈中存储了指针,该指针指向堆中的该实体的起始地址,当解释器寻找引用值时,
会首先检索其在栈中的地址,取得地址后从堆中获得实体
深浅拷贝
深浅拷贝只针对Object或者Array这样的引用数据类型
浅拷贝只复制指向对象的指针地址,而不是复制对象本身,新旧对象还是共享同一块内存。
深拷贝会创造另外一个一摸一样的对象,新旧对象不共用同一块内存,修改新对象不会影响到旧对象。
浅拷贝与赋值的区别
var obj1 = {name: 'LiLy', age: '12', language: ['english', 'chinese', 'frech']}
-
赋值: 赋的是该对象在栈中的地址,而不是堆中的数据。
两个对象指向的是同个存储空间,无论哪个对象发生改变,其实改变的都是原对象,是联动的。var obj2 = obj1 obj2.name = 'Lucy' obj2.language[0] = 'India' obj1 和 obj2 都是 { age: "12" language: (3) ["India", "chinese", "frech"] name: "Lucy" }
浅拷贝:按位拷贝对象,它会创建一个对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本数据类型,拷贝的就是基本类型的值;
如果属性是引用数据类型,拷贝的就是内存地址。
因此如果其中一个对象改变了这个内存地址,就会影响到另一个对象
function shallowCopy(src) {
var dst = {}
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop]
}
}
return dst
}
var obj3 = shallowCopy(obj1)
obj3.name = 'Bob'
obj3.language[0] = 'lan3'
obj3: {
age: "12"
language: (3) ["lan3", "chinese", "frech"]
name: "Bob"
}
obj1: {
age: "12"
language: (3) ["lan3", "chinese", "frech"]
name: "Lucy"
}
总结:
-- | 和原数据是否指向同个对象 | 第一层数据为基本数据类型 | 元数据中包含子对象 |
---|---|---|---|
赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
浅拷贝实现方式
- Object.assign()
把任意多个源对象的可枚举属性,拷贝给目标对象,然后返回目标对象。其中拷贝的是对象属性的引用,而不是对象属性身。
var obj1= {
age: "12",
language: (3) [1, 2, 3],
name: "a",
love: {son: 'tom'}
};
var obj2=Object.assign({}, obj1); // obj2: {age: "12", language: undefined, name: "a",love: {son: "tom"}}
obj2.love.son='Bob'; // obj2: {age: "12", language: undefined, name: "a",love: {son: "Bob"}}, obj1: {age: "12", language: undefined, name: "a",love: {son: "Bob"}}
// 当object 只有一层的时候,是深拷贝
obj2.age='13' // obj2: {age: "13", language: undefined, name: "a"}, obj1: {age: "12", language: undefined, name: "a"}
- Array.prototype.concat()
修改新对象的子对象会改到原对象
var arr1=[1,2,{name: 'a1'}];
var arr2=arr1.concat(); // arr2: [1,2,{name: 'a1'}]
arr2[1] = 3; // arr1:[1,2,{name: 'a1'}], arr2: [1,3,{name: 'a1'}]
arr2[2].name='a2'; // arr1:[1,2,{name: 'a2'}], arr2: [1,3,{name: 'a2'}]
- Array.prototype.slice()
修改新对象的子对象会改到原对象
var arr1=[1,2,{name: 'a1'}];
var arr2=arr1.slice(); // arr2: [1,2,{name: 'a1'}]
arr2[1] = 3; // arr1:[1,2,{name: 'a1'}], arr2: [1,3,{name: 'a1'}]
arr2[2].name='a2'; // arr1:[1,2,{name: 'a2'}], arr2: [1,3,{name: 'a2'}]
深拷贝实现方式
- JSON.parse(JSON.stringfy())
先转换成JSON字符串再转成JSON对象,会产生新的对象,而且对象会开辟新的栈,实现深拷贝。
注意: 不能处理函数,因为JSON.stringfy()不能结束函数,转化结果为null
var arr = [1, 3, {
username: ' kobe'
}];
var arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan'; // arr4:[1, 3, {username: 'duncan'}], arr: [1, 3, {username: 'kobe'}]
arr4[1]=2; // arr4:[1, 2, {username: 'duncan'}], arr: [1, 3, {username: 'kobe'}]
- 函数库lodash
_.cloneDeep函数实现深拷贝
var obj1={a: 's', {name: 'Lily'}};
var obj2 = _.cloneDeep(obj1);
- 手写递归方法
遍历对象,数组,直到里面都是基本数据类型,再进行复制
function checkedType (target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
function clone (target) {
// result 为最后返回的结果, tartgetType为目标数据类型
let result, tartgetType = checkedType(target)
if (tartgetType === 'Array') {
result = []
} else if (tartgetType === 'Object') {
result = {}
} else {
// 基本数据类型,直接返回结果
return target
}
for (var key in target) {
// 遍历对象或数组,直到找到基本数据类型进行复制
let value = target[key]
if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
//继续遍历获取到value值
result[i] = clone(value)
} else {
result[i] = value
}
}
return result
}