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开发技术大全 第4章 直接量与字符集
Jul 03 Javascript
Ubuntu 11.10 安装Node.js的方法
Nov 30 Javascript
JavaScript生成GUID的多种算法小结
Aug 18 Javascript
js获取电脑分辨率的思路及操作
Nov 22 Javascript
JavaScript中的原型和继承详解(图文)
Jul 18 Javascript
AspNet中使用JQuery上传插件Uploadify详解
May 20 Javascript
jQuery实现自动滚动到页面顶端的方法
May 22 Javascript
jQuery实现瀑布流布局详解(PC和移动端)
Sep 01 Javascript
bootstrap 点击空白处popover弹出框隐藏实例
Jan 24 Javascript
详解如何在vue-cli中使用vuex
Aug 07 Javascript
vue element upload实现图片本地预览
Aug 20 Javascript
JS简单表单验证功能完整示例
Jan 26 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
php检测useragent版本示例
2014/03/24 PHP
跟我学Laravel之路由
2014/10/15 PHP
微信公众平台DEMO(PHP)
2016/05/04 PHP
PHP7如何开启Opcode打造强悍性能详解
2018/05/11 PHP
关于实现代码语法标亮 dp.SyntaxHighlighter
2007/02/02 Javascript
javascript实现 在光标处插入指定内容
2007/05/25 Javascript
JavaScript中SQL语句的应用实现
2010/05/04 Javascript
JS判断不能为空实例代码
2013/11/26 Javascript
Jquery使用val方法读写value值
2015/05/18 Javascript
jquery仿苹果的时间/日期选择效果
2017/03/08 Javascript
微信小程序时间控件picker view使用详解
2018/12/28 Javascript
浅谈Vue.js中如何实现自定义下拉菜单指令
2019/01/06 Javascript
解决layer.confirm快速点击会重复触发事件的问题
2019/09/23 Javascript
js实现圆形显示鼠标单击位置
2020/02/11 Javascript
[47:04]LGD vs infamous Supermajor小组赛D组 BO3 第二场 6.3
2018/06/04 DOTA
python3使用scrapy生成csv文件代码示例
2017/12/28 Python
python 魔法函数实例及解析
2019/09/25 Python
Tensorflow实现多GPU并行方式
2020/02/03 Python
Django serializer优化类视图的实现示例
2020/07/16 Python
PyQt5的相对布局管理的实现
2020/08/07 Python
python如何实现DES加密
2020/09/21 Python
html5 canvas实现跟随鼠标旋转的箭头
2016/03/11 HTML / CSS
质检部职责
2013/12/28 职场文书
运动会通讯稿400字
2014/01/28 职场文书
母亲节感恩寄语
2014/02/21 职场文书
党的群众路线教育学习材料
2014/05/12 职场文书
平面设计师岗位职责
2014/09/18 职场文书
大学生考试作弊检讨书
2014/09/21 职场文书
2014年卫生保健工作总结
2014/12/08 职场文书
2015大学生党员自我评价范文
2015/03/03 职场文书
2015年班主任个人工作总结
2015/03/31 职场文书
学雷锋活动简报
2015/07/20 职场文书
同步小康驻村工作简报
2015/07/20 职场文书
SQLServer2019 数据库环境搭建与使用的实现
2021/04/08 SQL Server
JS Canvas接口和动画效果大全
2021/04/29 Javascript
如何利用 CSS Overview 面板重构优化你的网站
2021/10/24 HTML / CSS