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 相关文章推荐
SyntaxHighlighter代码加色使用方法
Sep 07 Javascript
jquery 学习笔记 传智博客佟老师附详细注释
Sep 12 Javascript
js 金额格式化来回转换示例
Feb 23 Javascript
浅谈jquery回调函数callback的使用
Jan 30 Javascript
jQuery的remove()方法使用详解
Aug 11 Javascript
jQuery-1.9.1源码分析系列(十)事件系统之事件包装
Nov 20 Javascript
基于JavaScript实现动态添加删除表格的行
Feb 01 Javascript
JavaScript的Backbone.js框架入门学习指引
May 07 Javascript
JQuery通过后台获取数据遍历到前台的方法
Aug 13 jQuery
angular的输入和输出的使用方法
Sep 22 Javascript
vue组件三大核心概念图文详解
May 30 Javascript
VSCode 配置uni-app的方法
Jul 11 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
kohana框架上传文件验证规则写法示例
2014/07/14 PHP
destoon实现底部添加你是第几位访问者的方法
2014/07/15 PHP
destoon实现会员商铺中指定会员或会员组投放广告的方法
2014/08/21 PHP
yii用户注册表单验证实例
2015/12/26 PHP
php操作access数据库的方法详解
2017/02/22 PHP
PHP读取XML格式文件的方法总结
2017/02/27 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
2020/02/21 PHP
Yii 实现数据加密和解密
2021/03/09 PHP
JQuery 操作select标签实现代码
2010/05/14 Javascript
某人初学javascript的时候写的学习笔记
2010/12/30 Javascript
腾讯UED 漂亮的提示信息效果代码
2011/09/12 Javascript
Extjs4 GridPanel的主要配置参数详细介绍
2013/04/18 Javascript
setTimeout函数的神奇使用
2017/02/26 Javascript
理顺8个版本vue的区别(小结)
2018/09/17 Javascript
Layui 动态禁止select下拉的例子
2019/09/03 Javascript
Ant Design Vue 添加区分中英文的长度校验功能
2020/01/21 Javascript
基于JavaScript或jQuery实现网站夜间/高亮模式
2020/05/30 jQuery
Python常见数据结构详解
2014/07/24 Python
Python实现屏幕截图的代码及函数详解
2016/10/01 Python
使用python存储网页上的图片实例
2018/05/22 Python
Python实现监控Nginx配置文件的不同并发送邮件报警功能示例
2019/02/26 Python
Python基础之变量基本用法与进阶详解
2020/01/03 Python
python中JWT用户认证的实现
2020/05/18 Python
Python页面加载的等待方式总结
2021/02/28 Python
处理HTML5新标签的浏览器兼容版问题
2017/03/13 HTML / CSS
巴西购物网站:Submarino
2020/01/19 全球购物
保加利亚运动鞋购物网站:SneakerStudio.bg
2020/12/23 全球购物
部队学习十八大感言
2014/01/11 职场文书
企业读书活动总结
2014/06/30 职场文书
党支部书记四风问题整改措施
2014/09/24 职场文书
庆祝儿童节标语
2014/10/09 职场文书
售房协议书范本2014
2014/10/23 职场文书
婚宴父亲致辞
2015/07/27 职场文书
2017年大学生寒假社会实践活动总结
2016/04/06 职场文书
合同范本之电脑出租
2019/08/13 职场文书
用 Python 定义 Schema 并生成 Parquet 文件详情
2021/09/25 Python