ES 基础 —— 函数参数的求值策略
什么是求值策略
函数参数的求值策略(Evalution Strategy)解决函数的参数如何求值的问题。
三种求值策略
按值传递(传值)
按值传递(call by value)是最常用的求值策略:函数的形参是被调用时所传实参的副本。
修改函数形参的值并不会影响实参。
按引用传递(传引用)
按引用传递(call by reference):函数的形参接受实参的隐式引用,而不再是副本,形参和实参指向相同的值。
修改函数形参的值,实参也会被修改。
按共享传递(传引用的副本)
按共享传递(call by sharing),最早由Barbara Liskov 在 1974 年的 GLU 语言中提出。该求值策略还被用于 Python、Java、Ruby、Scheme 等多种语言。 该策略的要点是:函数的形参是被调用时所传实参的引用的副本。
+-----+
+--------------------+ bar |
| +-----+
v ^
+------------+
| Object | | COPY VALUE
+------------+
^ |
| +-----+
+--------------------+ foo |
+-----+
图中 foo 和 bar 的都是 Object 的引用,foo 是从 bar 复制而来的。
它的特性:
- 对函数形参的修改,会影响实参。
- 对函数形参赋值,不会影响到实参。
ES 中函数的求值策略
- 基本数据类型:Undefined, Null, Boolean, Number, String,Symbol
- 引用数据:Object
基本数据类型
ES 中的基本数据类型,是按值传递的。
const bar = 0
function foo(i) {
i = 1
}
foo(bar)
console.log(bar) // 输出 0,没有受到 i = 1 的影响。
引用数据类型
用一个修改 Object 的操作来当作示例:
const bar = {
x: 0
}
function foo(obj) {
obj.x = 1
}
foo(bar)
console.log(bar.x) // 输出1,说明值被修改了。
这说明,obj
和 bar
引用了同一个对象。这好像是按引用传递,但到底是不是呢?
再看一例:
const bar = {
x: 0
}
function foo(obj) {
obj = 100000
}
foo(bar)
console.log(bar.x) // 输出 0,说明值没被修改。
如果是按引用传递,修改形参 obj
的值,应该影响到实参 bar
才对,可是 obj
的修改并没有影响到 bar
。 第一个例子和第二个例子存在矛盾,所以,ES 中的引用类型肯定不是按引用传递的。
再通过按共享传递的策略来解释上一节的两个例子。
const bar = {
x: 0
}
function foo(obj) {
obj.x = 1
}
foo(bar)
console.log(bar.x) // 输出1,说明值被修改了。
obj
是 bar
的一个副本,它们引用相同的对象,所以通过修改形参 obj
的属性值,能够影响到实参的属性值。
const bar = {
x: 0
}
function foo(obj) {
obj = 100000
}
foo(bar)
console.log(bar.x) // 输出 0,说明值没被修改。
obj
是 bar
的一个副本,它们引用相同的对象,对 obj
重新赋值只是切断了它和对象之间的联系,并不会影响对象。
之前的矛盾解释通了。所以,ES 中的引用类型是按共享传递的。
结论
ES 中的函数求值策略:
- 基本数据类型按值传递。
- 引用数据类型按共享传递。
也有另一种说法:“ES 中,不管基本数据类型,还是引用数据类型,都是按值传递的。” 这样,也可以解释得通 —— 引用数据类型按值传递了内存指针的副本。