JS的传值和传址
在使用一个变量给另一个变量赋值时,根据变量类型不同可以分为传址和传值两种传递方式。基本数据类型都是传值,对象,数组都是传址。
传值
顾名思义,传值就是把变量所在的内存里面的值传给另一个变量
var a=1;
var b=a; //b=1
在上面的例子中,b另外开辟了一个空间来存储a的值,因此,对a和b的操作时相互独立的,对其中任何一个进行操作都不会影响对方。
传址
顾名思义,传址传的是变量的地址,而不是内存空间里面的值。对象都是通过引用存储和赋值的,变量存储的不是对象本身,而是对象在内存中的地址,即引用。对象在赋值时,将对象的引用传递给另外一个变量。
对象就像时一个带锁的抽屉,抽屉里面的东西就是对象里面的值,钥匙就是对象的地址,在复制时,只是把钥匙复制了一份,两把钥匙作用的是同一个抽屉,因此,两个引用的操作会互相影响。这就是浅拷贝。
var a={};
var b=a;
对象的浅拷贝和深拷贝
浅拷贝
如果只复制了对象的引用,这时对象的内容只有一份,持有该引用的变量都可以对对象进行操作并会互相影响。
深拷贝
使用 for in 进行深拷贝
为了避免对象被随意更改,我们在复制对象的时候,更多的在内存中再开辟出一个空间,然后将原来对象的属性和值都复制到新的空间中去,在最原始级别下复制对象。
我们可以使用 for in 循环来实现。
let user = {
name: "John",
age: 30
};
let clone = {}; // 新的空对象
// 复制所有的属性值
for (let key in user) {
clone[key] = user[key];
}
// 现在的复制是独立的了
clone.name = "Pete"; // 改变它的值
alert( user.name ); // 原对象属性值不变
使用Object.assign 进行深拷贝
assign的语法
Object.assign(dest,[ src1, src2, src3...])
dest是复制之后的对象。dest后面的参数是要复制的对象,这里可以同时复制多个对象,如果他们有相同的属性,那么该属性只会保留最新的赋值。
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
// 把 permissions1 和 permissions2 的所有属性都拷贝给 user
Object.assign(user, permissions1, permissions2);
// 现在 user = { name: "John", canView: true, canEdit: true }
两种深拷贝方式存在的问题
我们在复制对象时新建了一个空间来存储对象,因此两个对象按理说是不会互相影响的。但是,万一对象的某个属性也是一个对象呢,那我们在复制时只不过把同一个对象的引用写在了两个地方,这里还是浅拷贝。
在复制对象的属性时,我们要逐个判断该属性是不是一个对象,如果属性是一个对象,那么也要对它进行拷贝。
到这里才是完完全全的深拷贝。