首先看一段代码
function test(x) {
x=2 //or set arguments[0]=2;
console.log(x,arguments[0]);
}
test(1)
执行结果中可以发现,argument和函数的参数列表中的变量是有关联的,这里假设函数的实参和形参个数是一样的。这个结果的背后到底是怎么回事,看了一下es5的arguments文档,尝试分析一下这个问题。 根据文档,当执行一个函数的时候,首先要执行CreateArgumentsObject 这个函数,并且把 1.当前执行的函数(也就是函数指针)fn 2.执行函数时用户实际传进来的参数列表args 3.函数声明中的实参列表名names 4.函数的变量环境对象env 这四个参数传进去,然后在CreateArgumentsObject 里,首先用Object函数创建一个空的对象obj,然后先往这个对象里挂载一些属性,其中包括但不限于: 1.设置该对象的Class为Arguments(这个我们用Object.prototype.toString.Call(arguments)可以看到) 2.设置该对象的构造函数为Object(通过arguments.constructor可以看到) 3.设置length为args的长度。 4.设置原型为Object.prototype 属性都是通过defineOwnProperty函数设置的,然后迭代实参列表,把每一个实参元素存起来,不过,这个并不仅存储在obj对象里。还重新生成一个对象parameterMap来存储,每一步具体的操作为: 1.把实参元素存储到obj里。 2.判断当前的索引是否小于函数fn实参变量names的长度,如果是就把实参元素存储一份到parameterMap里,并且设置描述符为{[[Set]]: p, [[Get]]: g, [[Configurable]]: true},es5说到这句话Let g be the result of calling the MakeArgGetter abstract operation with arguments name and env,也就是说p,g是一个用函数生成的函数,并且存在一个闭包,以后访问的时候是去访问env里面的值。到这里我们还没看出什么问题,arguments对象看起来差不多是这样的
arguments = {
Class: 'Arguments',
constructor: Object,
0: 'hello',
1: 'world',
length: 2
.
.
.
ParameterMap: {
0: getter/setter
1: getter/setter
}
}
接着es5里说到了重点,原话The [[Get]] internal method of an arguments object for a non-strict mode function with formal parameters when called with a property name P performs the following steps,具体的可以看文档,其中说到了当访问arguments的属性时,会调用内部的arguments的get方法,这个方法会到ParameterMap对象里面找值,所以这里就会用到getter/setter,所以不管我们操作的变量是arguments[0],还是x,对应的值都是env里的那个变量,也就是arguments[0]和x共享了一个值,所以他们的操作会互相影响。 文档最后也提到,因为这种关联是进入函数时建立的,所以我们可以通过删除后重定义去解绑,上面的这些是基于非严格模式的,严格模式下他两的独立的,不会相互影响。