javascript管中窥豹 形参与实参浅析


Posted in Javascript onDecember 17, 2011

引子:
今天看到别人的一个题目:

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 
}

带参数的情况
带参数的情况 fn_2(1); 
function fn_2(x){//arguments赋值完成,arguments[0]=1。同时形参x有值,两者相关联,永结同心。 
var x = 3;//x赋值为3,x与arguments[0]关联,于是arguments[0]被赋值为3,。 
console.log(x,arguments[0]);//打印x=3,arguments[0] = 3 
arguments[0] = 2;//arguments[0]被赋值2,由于x与arguments[0]已经关联到一起,于是x同时改变 
console.log(x,arguments[0]);//打印x = 2,arguments[0]=2 
}

反过来应该也是一样的:
不带参数
fn_2(); 
function fn_2(x){//不关联 
arguments[0] = 2;//找不到对应的x(undefined),相互独立 
console.log(x,arguments[0]);//undefined,2 
x = 3;//相互独立,快照。虽然arguments动态添加了,老死不相往来,所以依旧失败 
console.log(x,arguments[0]);//3,2 
}

带参数
fn_2(1); 
function fn_2(x){ 
arguments[0] = 2;//关联 
console.log(x,arguments[0]);//2,2 
x = 3;//关联 
console.log(x,arguments[0]);//3,3 
}

由于我们只有一个形参,可能说服力不够,现在增加到两个。
只有一个实参的情况:
fn_2(1); 
function fn_2(x,y){ //arguments赋值完成,arguments[0]=1,arguments[1]=undefined,因此只有x与arguments[0]关联,y与arguments[1]老死不往来 
console.log(x,y,arguments[0],arguments[1]); //1,undefined,1,undefined 
var x = 3; //x赋值为3,x与arguments[0]关联,于是arguments[0]被赋值为3。 
console.log(x,y,arguments[0],arguments[1]); //3,undefined,3,undefined 
var y = 4; //y赋值为3,y与arguments[1]相互独立,arguments[1]还是为undefined 
console.log(x,y,arguments[0],arguments[1]); //3,4,3,undefined 
arguments[0] = 2; //arguments[0]被赋值2,由于x与arguments[0]已经关联到一起,于是x同时改变 
console.log(x,y,arguments[0],arguments[1]); //2,4,2,undefined 
arguments[1] = 5; //arguments[1]被赋值5,y与arguments[1]相互独立,于是y还是保持为4 
console.log(x,y,arguments[0],arguments[1]); //x=2,y=4,arguments[0]=2,arguments[1]=5 
}

有两个实参的情况:
fn_3(1,6); 
function fn_3(x,y){ //arguments赋值完成,arguments[0]=1,arguments[1]=6,x与arguments[0],y与arguments[1]都相互关联 
console.log(x,y,arguments[0],arguments[1]); //1,6,1,6 
var x = 3; //x赋值为3,x与arguments[0]关联,于是arguments[0]被赋值为3。 
console.log(x,y,arguments[0],arguments[1]); //3,6,3,6 
var y = 4; //y赋值为3,y与arguments[1]关联,于是arguments[1]被赋值为4。 
console.log(x,y,arguments[0],arguments[1]); //3,4,3,4 
arguments[0] = 2; //arguments[0]被赋值2,由于x与arguments[0]已经关联到一起,于是x同时改变 
console.log(x,y,arguments[0],arguments[1]); //2,4,2,4 
arguments[1] = 5; //arguments[1]被赋值5,由于y与arguments[1]已经关联到一起,于是y同时改变 
console.log(x,y,arguments[0],arguments[1]); //x=2,y=5,arguments[0]=2,arguments[1]=5 
}

以上全部是推测,因为实际中没有办法形参的信息,所以我按照推测写了一个小测试:
下面的也改了:
function _Function(){//获得的形参列表为数组:_args 
var _args = []; 
for(var i = 0; i < arguments.length - 1; i++){ 
var obj = {}; 
obj['key'] = arguments[i]; 
obj[arguments[i]] = undefined; 
_args.push(obj); 
} 
//this._argu = _args; 
var fn_body = arguments[arguments.length - 1]; 
//下面的方法获取实参_arguments,这里_arguments实现为一个数组,而非arguments对象 
this.exec = function(){ 
//函数运行时,实参_arguments被赋值 
var _arguments = []; 
for(var i = 0; i < arguments.length; i++){ 
_arguments[i] = arguments[i]; 
} 
//下面执行函数体 
eval(fn_body); 
} 
}

替换成:
function _Function(){//获得的形参列表为数组:_args 
var _args = []; 
for(var i = 0; i < arguments.length - 1; i++){ 
var obj = {}; 
obj['key'] = arguments[i]; 
obj[arguments[i]] = undefined; 
_args.push(obj); 
} 
//this._argu = _args; 
var fn_body = arguments[arguments.length - 1]; 
//下面的方法获取实参_arguments,这里_arguments实现为一个数组,而非arguments对象 
this.exec = function(){ 
//函数运行时,实参_arguments被赋值 
var _arguments = []; 
for(var i = 0; i < arguments.length; i++){ 
_arguments[i] = arguments[i]; 
} 
//在运行开始就判断关联信息 
for(var j = 0; j < Math.min(_arguments.length,_args.length); j++){ 
_args[j]["link"] = true; 
} 
//下面执行函数体 
eval(fn_body); 
} 
}

上面按理来说,关联应该是把两者指向同一个对象,可是我只需要分析例子,没打算做得那么精细,所以是在函数体里面用if语句判断的 。
把例子中fn_2换成对应的形式就是:
// function fn_2(x){ 
// var x = 3; 
// console.log(x,arguments[0]); 
// arguments[0] = 2; 
// console.log(x,arguments[0]); 
// } 
// fn_2(1) 
//在fn_2body中,用_args[i]["link"] = true;来表示形参与实参相关联 
var fn_2body = ''+ 
'_args[0][_args[0]["key"]] = 3;'+ 
'if(_args[0]["link"]){ _arguments[0] = _args[0][_args[0]["key"]];}' + 
'console.log(_args[0][_args[0]["key"]],_arguments[0]);'+ 
'_arguments[0] = 2;'+ 
'if(_args[0]["link"]){ _args[0][_args[0]["key"]] = _arguments[0]}' + 
'console.log(_args[0][_args[0]["key"]],_arguments[0]);'; 
var fn_2 = new _Function('x',fn_2body); 
fn_2.exec(1);

画了一张图来表示实例与改写函数两者的关系,顺便也改了一下:

javascript管中窥豹 形参与实参浅析

回到文章开头的例子:
function fn(x){ 
x = 10; 
arguments[0] = 20; 
console.log(x,arguments[0]) 
} 
fn()

显然,两者相互独立:
x = 10,arguments[0] = 20;
推测一下:
function fn(x){ 
x = 10; 
arguments[0] = 20; 
console.log(x,arguments[0]) 
} 
fn(1)

应该都是输出20,20
function fn(x){ 
arguments[0] = 20; 
console.log(x,arguments[0]) 
} 
fn(1)

应该也都是输出20,20
function fn(x){ 
arguments[0] = 20; 
console.log(x,arguments[0]) 
} 
fn()

应该是undefined和20
原文来自cnblogs小西山子
Javascript 相关文章推荐
javascript中的prototype属性实例分析说明
Aug 09 Javascript
关于js获取radio和select的属性并控制的代码
May 12 Javascript
javascript设计模式 封装和信息隐藏(上)
Jul 24 Javascript
JQuery的read函数与js的onload不同方式实现
Mar 18 Javascript
JavaScript获取图片的原始尺寸以宽度为例
May 04 Javascript
Javascript点击其他任意地方隐藏关闭DIV实例
Jun 21 Javascript
基于js中的存储键值对以及注意事项介绍
Mar 30 Javascript
vue-router history模式下的微信分享小结
Jul 05 Javascript
解决vue-cli项目开发运行时内存暴涨卡死电脑问题
Oct 29 Javascript
使用webpack/gulp构建TypeScript项目的方法示例
Dec 18 Javascript
Javascript执行流程细节原理解析
May 14 Javascript
Node.js API详解之 repl模块用法实例分析
May 25 Javascript
jquery focus(fn),blur(fn)方法实例代码
Dec 16 #Javascript
JS获取整个页面文档的实现代码
Dec 15 #Javascript
jQuery版仿Path菜单效果
Dec 15 #Javascript
cnblogs 代码高亮显示后的代码复制问题解决实现代码
Dec 14 #Javascript
js 可拖动列表实现代码
Dec 13 #Javascript
使用Mootools动态添加Css样式表代码,兼容各浏览器
Dec 12 #Javascript
分享一个用Mootools写的鼠标滑过进度条改变进度值的实现代码
Dec 12 #Javascript
You might like
如何用C语言编写PHP扩展的详解
2013/06/13 PHP
php实现保存周期为1天的购物车类
2017/07/07 PHP
使用prototype.js 的时候应该特别注意的几个问题.
2007/04/12 Javascript
捕获关闭窗口的脚本
2009/01/10 Javascript
使用Jquery Aajx访问WCF服务(GET、POST、PUT、DELETE)
2012/03/16 Javascript
JavaScript中的私有/静态属性介绍
2012/07/26 Javascript
一样的table?不一样的table(可编辑状态table)
2012/09/19 Javascript
同域jQuery(跨)iframe操作DOM(示例代码)
2013/12/13 Javascript
实例讲解JS中setTimeout()的用法
2016/01/28 Javascript
如何使用Bootstrap的modal组件自定义alert,confirm和modal对话框
2016/03/01 Javascript
jQuery插件实现可输入和自动匹配的下拉框
2016/10/24 Javascript
JS调用Android、Ios原生控件
2017/01/06 Javascript
浅谈jQuery中事情的动态绑定
2017/02/12 Javascript
jQuery树插件zTree使用方法详解
2017/05/02 jQuery
Node.js中的http请求客户端示例(request client)
2017/05/04 Javascript
如何理解Vue的作用域插槽的实现原理
2017/08/19 Javascript
vue-devtools的安装步骤
2018/04/23 Javascript
深入浅析JS中的严格模式
2018/06/04 Javascript
少女风vue组件库的制作全过程
2019/05/15 Javascript
Vue+Element UI+vue-quill-editor富文本编辑器及插入图片自定义
2019/08/20 Javascript
node+multer实现图片上传的示例代码
2020/02/18 Javascript
VueCli生产环境打包部署跨域失败的解决
2020/11/13 Javascript
对numpy数据写入文件的方法讲解
2018/07/09 Python
python 定时任务去检测服务器端口是否通的实例
2019/01/26 Python
django项目登录中使用图片验证码的实现方法
2019/08/15 Python
Python如何用wx模块创建文本编辑器
2020/06/07 Python
Python执行时间的几种计算方法
2020/07/31 Python
值得收藏的HTML5资源(学习html5的朋友可以收藏下)
2010/07/20 HTML / CSS
使用phonegap获取设备的一些信息方法
2017/03/31 HTML / CSS
TripAdvisor西班牙官方网站:全球领先的旅游网站
2018/01/10 全球购物
西班牙最大的婴儿用品网上商店:Bebitus
2019/05/30 全球购物
Andrew Marc官网:设计师外套的领先制造商
2019/10/30 全球购物
自动化专业职业生涯规划书范文
2014/01/16 职场文书
干部作风建设年活动剖析材料
2014/10/23 职场文书
小学四年级班务总结该怎么写?
2019/08/16 职场文书
MySQL Router实现MySQL的读写分离的方法
2021/05/27 MySQL