赋值策略 🌕

定义

赋值策略也叫做求值策略(evaluation strategy)。赋值是我们在编写 JavaScript 代码时最常用到的操作。而基本类型的赋值,跟对象的赋值是不一样的。赋值大概分为几种: 值传递,引用传递,共享传递。

值传递(call-by-value)

基本数据类型(undefined, null, Boolean, String, Number, Symbol)的赋值,一般都是值传递。代码如下:

1
2
3
4
5
6
var a = "hello ";
var b = a;
console.log(b); // hello
b = "world";
console.log(b); // world
console.log(a); // hello

从代码中,我们看到,把a的值赋值给bb的值与a「相似」,然后给b重新赋值 ,b的值改变,a的值不收影响。

以上过程,计算机大概做了这些事情:(看完你就会知道,为什么我上段话中为什么说的是「相似」而不是「相同」)

引用传递(call-by-reference)

对象数据类型(Object, Array, Function)的赋值,一般都是引用传递。什么意思呢?类比于值传递传递的是值,引用传递传递的是引用,类似于指针,也就是传递的是对象存储的地址信息。代码如下:

1
2
3
4
5
6
7
8
9
10
11
var a = { x: 1, y: 2 };
var b = a;
console.log(b); // {x:1, y:2}

b.x = 3;
console.log(b); //{x:3, y:2}
console.log(a); // {x:3, y:2}

b = { z: 4 };
console.log(b); // {z: 4}
console.log(a); // {x:3, y:2}

上面代码的结果,跟值传递的完全不一样。为什么设置b.x可以改变a.x的值?为什么设置b的值,a的值却没有改变?
带着问题,我们看一下计算机的执行流程。

共享传递(call-by-sharing)

共享传递其实是对地址信息拷贝的传递。回到引用传递的代码中,var a = { x: 1, y: 2 };是按引用传递,因为传递的是对象的引用,
var b = a;其实就是共享传递,因为它传递的是引用的拷贝。

小结

到这里,其实赋值策略大概就讲完了。其实说到底,我感觉所有的赋值其实都是一种值传递,只不过传递的东西类型不同吧。
或者是数据,或者是数据的地址,或者是数据地址的拷贝。之所以又这么三个清晰的概念,我猜是为了让我们能把赋值的流程更加清楚明白吧。
接下来,我们顺便延伸说一下函数的参数,里面包含了这三种传递的应用。

函数参数

ESLint 上面有这么一个规则:no-param-reassign
意思是建议不要对函数的参数进行重新赋值。为什么呢? 我们看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
var num = 10;
var obj1 = { item: "unchanged" };
var obj2 = { item: "unchanged" };
var changeStuff = function(a, b, c) {
a = a * 10;
b.item = "changed";
c = { item: "changed" };
};
changeStuff(num, obj1, obj2);
console.log(num); // 10
console.log(obj1.item); // changed
console.log(obj2.item); // unchanged

其实,函数的参数是对传入值的拷贝
比如改变参数a的值,num却不会被改变。
而参数b其实就是共享传递——b存储的其实是obj1内存储的对象地址信息的拷贝。