JavaScript对值和引用的赋值/传递在语法上没有什么区别,完全根据值的类型来决定。
举个例子:
// 简单值赋值
var a = 2;
var b = a; // b只是a的值的一个复本
b++;
a; // 2
b; // 3
例子中 2 是一个标量基本类型值,所有变量a持有该值的一个复本,b持有它的另外一个复本。 b更改时,a的值保持不变。
// 复合值赋值
var c = [1, 2, 3, 4];
var d = c; // d是[1, 2, 3, 4]的一个引用
d.push(5);
c; // 1, 2, 3, 4, 5
d; //1, 2, 3, 4, 5
c 和 d 则分别指向同一个复合值[1, 2, 3, 4]的两个不同的引用。 注意:c 和 d 仅仅是指向值 [1, 2, 3, 4], 并非持有。所以它们更改的是同一个值(例如调用.push(5)), 随后它们都指向更改后的新值[1, 2, 3, 4, 5]。
结论:
1、简单值(即基本类型值)总是通过值复制的方式类赋值/引用,包括 null、undefined、字符串、数字、布尔和ES6中的symbol。
2、复合值——对象(包括数组、封装对象)和函数,则总是通过引用复制的方式来赋值/传递。
我们要明白的是引用复制指向的是值本身而非变量,所以一个引用无法更改另一个引用的指向
var a = [1, 2, 3];
var b = a;
a; // [1, 2, 3]
b; // [1, 2, 3]
b = [4, 5, 6];
a; // [1, 2, 3]
b; // [4, 5, 6]
b = [4, 5, 6] 并不影响a指向值[1, 2, 3], 除非b不是指向数组的引用,而是指向a的指针,但是JavaScript中不存在这种情况!
有时候下面这种情况又让人很是困惑:
function foo (x) {
x.push(4);
x; // 1, 2, 3, 4
x = [4, 5, 6];
x.push(7);
x; // [4, 5, 6, 7];
}
var a = [1, 2, 3];
foo(a);
a; // 是[1, 2, 3, 4],不是[4, 5, 6, 7];
我们向函数传递a的时候,实际是将引用a的一个复本赋值给x,而a仍然指向[1, 2, 3]. 在函数中我们可以通过x来更改数组的值(.push(4)之后变为[1, 2, 3, 4])。但x = [4, 5, 6, 7] 并不影响a的指向,所有a仍然指向[1, 2, 3, 4]。
我们不能通过x来更改引用a的指向,只能更改a和x的共同指向的值。
如果我们要更改a的值为[4, 5, 6, 7],必须更改x指向的数组,而不是为x赋值一个新的数组。
function foo(x) {
x.push(4);
x; // [1, 2, 3, 4]
x.length = 0; // 清空数组
x.push([4, 5, 6, 7]);
x; //[4, 5, 6, 7]
}
var a = [1, 2, 3];
foo(a);
a; // 是[4, 5, 6, 7]而不是[1, 2, 3, 4]
从上例中我们可以看出,x.length = 0和x.push(4) 并没有创建新的数组,而是更改了当前数组。于是a指向的值变成了[4, 5, 6, 7]。
所以我们无法自行决定使用值赋值还是引用复制, 一切由值的类型来决定