引子:
今天看到别人的一个题目:
复制代码 代码如下:
function fn(x){
x = 10;
arguments[0] = 20;
console.log(x,arguments[0])
}
fn()
感觉自己对这也是一知半解,自己也可以试一下,于是就特地分析一下。
本想从语言的角度来分析,无奈功力不够,只能粗浅的尝试一下,于是称之管中窥豹,还望大牛指正。
这是昨天写的,今天吃饭的时候又想了一下,想来想去感觉有些问题还是说得不靠谱,于是又试着修改了一下。
每一本js入门书籍都会提到,JS的函数内部有一个Arguments的对象arguments,用来函数调用的时候实际传入函数的参数,fn.length保存形参的长度。
这些对分析来说略有用处,可是我想得到更多形参的信息,不知道有谁有比较好的办法,我暂时无解。
于是只能模拟了。
先不理会模拟,从实际问题出发:
复制代码 代码如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<script type="text/javascript">
//形参中含有隐形的声明var x = undefined
function fn(x){
console.log(x,arguments[0]);
arguments[0] = 2;
console.log(x,arguments[0]);
}
console.log('fn():');
fn();
//undefined , undefined
//undefined , 2
console.log('fn(1):');
fn(1);
//1,1
//2,2
重点关注后面两个函数(fn_1,fn_2)的执行,在这里,我们直接重新声明了形参对应的x,看到网上有的人说这是声明的一个局部变量x。
也是,不过这个局部变量不是一般的局部变量,x直接关联对应的arguments,上面的实例中就是x关联arguments[0];
所以我猜测这个赋值的流程应该是
1、函数定义的时候,声明了形参,如果函数体内有相同名称的局部变量,则忽略此声明。同时函数体内同时会有一个对象arguments;
(乱入一句:个人以为arguments当初不定义成数组的一个考虑是否是因为在函数定义内无法确定实际参数的个数[运行时动态确定],那么要么这个数组无限大,要么数组一取值就越界)。
回到正题:
对于fn_2,初始化形参相当于var x;(此时x没有赋值,默认为undefined,赋值是在语句执行的时候赋值的)
所以如果可以这么写的话,fn_2就应该是这样:
复制代码 代码如下:
function fn_2(var x){
x = 3;
console.log(x,arguments[0]);
arguments[0] = 2;
console.log(x,arguments[0]);
}
2、函数语法检测通过,执行的时候,函数内部的arguments对象一开始就得到赋值,赋值完毕后,函数体内的语句开始执行。
下面的一段表述是我自己想的,不知道正确不正确(特别是关联的说法):
复制代码 代码如下:
一旦发现形参(对应的变量)被赋值,那么会去寻找arguments对应的项,如果发现了arguments对应的项,那么设置形参与arguments对应项的关联。如果没有发现arguments里面对应的项(undefined),那么形参和arguments还是保持独立。这里寻找的是arguments在函数运行开始的一个快照。反过来arguments赋值也是一样。
上面的删除的部分是昨天的,红字部分是写到一半的时候发现有问题加上去的。今天回过神来,昨天为什么要傻逼的想到快照呢,这个不就是函数开始运行时直接
判断关联么?于是改了一下表述:
复制代码 代码如下:
在函数开始执行时,设置形参与arguments的关联信息。如果形参与对应的arguments里面能找到对应的项(均为undefined),那么两者关联。后面不论怎么处理,都不会改变整个函数体内的关联信息。
于是后面的实例说明的说法也要改变:
回到例子,fn_2函数语法检测通过,从第二步开始执行:
不带参数的情况
复制代码 代码如下:
fn_2();
function fn_2(x){//arguments赋值完成,由于没有实参,于是arguments参数列表为空。同时判断关联信息,显然形参有,arguments空,两者相互独立,以后都不会再关联
var x = 3;//x赋值为3,x与arguments[0]相互独立,arguments[0]还是为undefined
console.log(x,arguments[0]);//打印x=3,arguments[0]为undefined
arguments[0] = 2;//arguments被赋值,x与arguments[0]相互独立。因此x=3不改变
console.log(x,arguments[0]);//打印x = 3,arguments[0]=2
}
带参数的情况
复制代码 代码如下: