定义
赋值策略也叫做求值策略(evaluation strategy)。赋值是我们在编写 JavaScript 代码时最常用到的操作。而基本类型的赋值,跟对象的赋值是不一样的。赋值大概分为几种: 值传递,引用传递,共享传递。
值传递(call-by-value)
基本数据类型(undefined
, null
, Boolean
, String
, Number
, Symbol
)的赋值,一般都是值传递。代码如下:
1 | var a = "hello "; |
从代码中,我们看到,把a
的值赋值给b
,b
的值与a
「相似」,然后给b
重新赋值 ,b
的值改变,a
的值不收影响。
以上过程,计算机大概做了这些事情:(看完你就会知道,为什么我上段话中为什么说的是「相似」而不是「相同」)
引用传递(call-by-reference)
对象数据类型(Object, Array, Function)的赋值,一般都是引用传递。什么意思呢?类比于值传递传递的是值,引用传递传递的是引用,类似于指针,也就是传递的是对象存储的地址信息。代码如下:
1 | var a = { x: 1, 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 | var num = 10; |
其实,函数的参数是对传入值的拷贝。
比如改变参数a
的值,num
却不会被改变。
而参数b
其实就是共享传递——b
存储的其实是obj1
内存储的对象地址信息的拷贝。