Description
前文:在一次团队code review过程中发现了一个问题:对函数参数的重新分配。airbnb的代码风格指导中是不允许这种行为的,本着求知的精神,我去查了下这个行为具体会产生哪些影响,其中一篇博客解释了该行为。
你知道JavaScript中函数的命名参数变量与函数的Arguments对象是相互关联的吗?
我在尝试使用一个函数时遇到了这个问题,该函数在使用时允许你传递两个或三个参数,如果只传递两个参数,则为第一个参数提供默认值。
var makePerson = function(favoriteColor, name, age) {
if (arguments.length < 3) {
favoriteColor = "green";
name = arguments[0];
age = arguments[1];
}
return {
name: name,
age: age,
favoriteColor: favoriteColor
};
};
var person = makePerson("Joe", 18);
console.log(JSON.stringify(person));
// => {"name":"green","age":"green","favoriteColor":"green"}
奇怪的是最终结果中所有的属性的值都被设置成了“green”,但我想要的是:
=> {"name":"Joe","age":18,"favoriteColor":"green"}
从结果上来看,此时发生的情况是,当我将 favoriteColor 设置成 “green" 时,同时也影响到了 arguments[0] 的值,arguments[0] 也被设置成了 ”green",当我执行 name = arguments[0] 时,情况变得更糟糕,因为 name 的修改,影响到了 arguments[1],并把 arguments[1] 也设置成了 “green”。
我没有意识到命名参数与Arguments对象是相互关联的。这里有一个很好的解释来说明这一现象:
Arguments对象的属性与函数命名参数同义。它们都引用堆栈中的相同地址。如果函数体具有通过名称引用或arguments[]数组引用更改参数值的代码,则两个引用的值将反映相同的值。
注:JavaScript高级程序设计对这段有不同的说明。下面是高程原句:“这并不是说读取这两个值会访问相同的内存空间;它们的内存空间是独立的,但他们的值会同步”。另外,高程还提到,严格模式下对arguments[]赋值会报错,且如果你修改了命名参数的值,对应的arguments[]不会同时修改。
无论使用何种语言,在函数内重新分配参数变量通常都不是一个好主意。
在JavaScript中,事实证明这是一个非常糟糕的主意。
注:总结就是,JavaScript中函数的命名参数变量与函数的Arguments对象是相互关联的,在非严格模式下,当你改了一个值,另一也会跟着改变。如果在代码中同时使用了这两个,且未注意到这个问题,就可能写出有bug的代码。