要区分是赋值还是浅拷贝和深拷贝首先要理解一下几个概念
ECMAScript中的数据类型可分为两种:
基本类型:undefined,null,Boolean,String,Number,Symbol
引用类型:Object,Array,Date,Function,RegExp等
堆和栈
栈(stack)为自动分配的内存空间,它由系统自动释放;
堆(heap)则是动态分配的内存,大小不定也不会自动释放。
不同类型的存储方式:
基本类型:基本类型值在内存中占据固定大小,保存在栈内存中
引用类型:引用类型的值是对象,保存在堆内存中,而栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址
值传递
方法调用时,实际参数把它的值传递给对应的形式参数,函数接收的原始值得一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值得修改,不影响实际参数的值
引用传递
一般也称为地址传递。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的原始值的内存地址, 在方法执行中,形参和实参内容相同,只想同一块内存地址,方法执行中对引用的操作将影响到实际对象。
什么是深拷贝和浅拷贝
基本类型
赋值
let foo = 1;
let bar = foo;
console.log(foo === bar); // -> true
let foo = 233;
console.log(foo); // -> 233
console.log(bar); // -> 1
基本数据类型值不可变
基本类型的比较是值的比较
引用类型
引用类型的赋值是传址
let a = [1,2,3];
let b = a;
console.log(b); // [1, 2, 3]
引用对象的指针指向同一个对象,项目之间有影响
浅拷贝
对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
重新在堆中创建内存,拷贝后对象的基本类型互不影响,但是不能对对象中的子对象进行拷贝
1.手工浅拷贝
function simpleClone(initalObj) {
let obj = {};
for ( let i in initalObj) {
obj[i] = initalObj[i];
}
return obj;
}
let obj = {
a: "hello",
b:{
a: "world",
b: 21
},
c:["Bob", "Tom", "Jenny"],
d:function() {
alert("hello world");
}
}
let cloneObj = simpleClone(obj);
console.log(cloneObj.b); //{a: "world", b: 21}
console.log(cloneObj.c); //["Bob", "Tom", "Jenny"]
console.log(cloneObj.d); //ƒ () {alert("hello world");}
cloneObj.b.a = "changed";
cloneObj.c = [1, 2, 3];
cloneObj.d = function() { alert("changed"); };
console.log(obj.b); //{a: "changed", b: 21}
console.log(obj.c); //["Bob", "Tom", "Jenny"]
console.log(obj.d); //ƒ () {alert("hello world");}
2.Object.assign()
Object.assign(target,...source) 返回值 目标对象
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
let obj = {a: {b: "javascript", c: 24}};
let newObj = Object.assign({}, obj);
newObj.a.b = "Hello";
console.log(obj.a.b) //Hello
深拷贝
对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
实现深拷贝 的几种方法:
1.JSON.parse()和JSON.stringify()
let obj = {
name: 'leeper',
age: 20,
friend: {
name: 'lee',
age: 19
}
};
let copyObj = JSON.parse(JSON.stringify(obj));
obj.name = 'Sandman';
obj.friend.name = 'Jerry';
console.log(obj); // {name: "Sandman", age: 20, friend: {age: 19,name: 'Jerry'}}
console.log(copyObj); // {name: "leeper", age: 20, friend: {age: 19,name: 'lee'}}
以简单的方法做到深拷贝,但这种方法也有不少坏处 抛弃了对象的constructor
只能处理Number String Boolean Array 扁平对象等能被Json表示的数据结构,RegExp对象是无法通过这种方式深拷贝,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON。
2.递归拷贝
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
function deepClone(target) {
let result, targetType = checkedType(target)
if (targetType === 'object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
return target
}
for (let i in target) {
let value = target[i]
if (checkedType(value) === 'Object' ||
checkedType(value) === 'Array') {
result[i] = deepClone(value)
} else {
result[i] = value;
}
}
return result
}
递归实现深拷贝但是当遇到两个互相引用的对象,会出现死循环的情况
深拷贝与浅拷贝的区别
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象