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 密码强弱度检测万能插件
Feb 25 Javascript
基于jquery的Repeater实现代码
Jul 17 Javascript
document.getElementBy(&quot;id&quot;)与$(&quot;#id&quot;)有什么区别
Sep 22 Javascript
javascript 操作符(~、&amp;、|、^、)使用案例
Dec 31 Javascript
浅谈Javascript线程及定时机制
Jul 02 Javascript
深入理解Vuex 模块化(module)
Sep 26 Javascript
js时间戳与日期格式之间转换详解
Dec 11 Javascript
webpack将js打包后的map文件详解
Feb 22 Javascript
微信小程序实现搜索指定景点周边美食、酒店
May 18 Javascript
React中使用外部样式的3种方式(小结)
May 28 Javascript
react antd表格中渲染一张或多张图片的实例
Oct 28 Javascript
js重写alert事件(避免alert弹框标题出现网址)
Dec 04 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
CodeIgniter php mvc框架 中国网站
2008/05/26 PHP
php二维码生成
2015/10/19 PHP
PHP图片添加水印功能示例小结
2016/10/03 PHP
php 字符串中是否包含指定字符串的多种方法
2018/04/12 PHP
PHP 布尔值的自增与自减的实现方法
2018/05/03 PHP
js每次Title显示不同的名言
2008/09/25 Javascript
让IE8支持DOM 2(不用框架!)
2009/12/31 Javascript
javaScript checkbox 全选/反选及批量删除
2010/04/28 Javascript
清空上传控件input file的值
2010/07/03 Javascript
js+css在交互上的应用
2010/07/18 Javascript
Raphael带文本标签可拖动的图形实现代码
2013/02/20 Javascript
js不能跳转到上一页面的问题解决方法
2013/03/01 Javascript
JS中的Replace方法使用经验分享
2015/05/20 Javascript
jquery图片倾斜层叠切换特效代码分享
2015/08/27 Javascript
React Native中TabBarIOS的简单使用方法示例
2017/10/13 Javascript
JS实现在文本指定位置插入内容的简单示例
2017/12/22 Javascript
JavaScript设计模式之调停者模式实例详解
2018/02/03 Javascript
Vue中的vue-resource示例详解
2018/11/02 Javascript
Vue使用lodop实现打印小结
2019/07/06 Javascript
5分钟教你用nodeJS手写一个mock数据服务器的方法
2019/09/10 NodeJs
[53:23]Secret vs Liquid 2018国际邀请赛淘汰赛BO3 第二场 8.25
2018/08/29 DOTA
Python中Collection的使用小技巧
2014/08/18 Python
Python Requests 基础入门
2016/04/07 Python
python并发编程之多进程、多线程、异步和协程详解
2016/10/28 Python
Python计算公交发车时间的完整代码
2020/02/12 Python
解决Django Haystack全文检索为空的问题
2020/05/19 Python
Python 处理日期时间的Arrow库使用
2020/08/18 Python
用Python进行websocket接口测试
2020/10/16 Python
HTML5是否真的可以取代Flash
2010/02/10 HTML / CSS
巴西香水和化妆品购物网站:The Beauty Box
2019/09/03 全球购物
估算杭州有多少软件工程师
2015/08/11 面试题
喜之郎果冻广告词
2014/03/20 职场文书
陕西导游词
2015/02/04 职场文书
自我评价优缺点范文
2015/03/11 职场文书
企业承诺书格式范文
2015/04/28 职场文书
JS高级程序设计之class继承重点详解
2022/07/07 Javascript