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 相关文章推荐
JS图片无缝滚动(简单利于使用)
Jun 17 Javascript
jQuery实现对无序列表的排序功能(附demo源码下载)
Jun 25 Javascript
微信小程序 获取设备信息 API实例详解
Oct 02 Javascript
js原生代码实现轮播图的实例讲解
Jul 28 Javascript
for循环 + setTimeout 结合一些示例(前端面试题)
Aug 30 Javascript
Vuejs 2.0 子组件访问/调用父组件的方法(示例代码)
Feb 08 Javascript
vue实现在表格里,取每行的id的方法
Mar 09 Javascript
详解js中Array的方法及技巧
Sep 12 Javascript
Vue.js特性Scoped Slots的浅析
Feb 20 Javascript
配置eslint规范项目代码风格
Mar 11 Javascript
浅谈Vue3 Composition API如何替换Vue Mixins
Apr 29 Javascript
vue使用better-scroll实现滑动以及左右联动
Jun 30 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实现建立多层级目录的方法
2014/07/19 PHP
屏蔽PHP默认设置中的Notice警告的方法
2016/05/20 PHP
PHP入门教程之自定义函数用法详解(创建,调用,变量,参数,返回值等)
2016/09/11 PHP
基于PHP实现生成随机水印图片
2020/12/09 PHP
一个js封装的不错的选项卡效果代码
2008/02/15 Javascript
combox改进版 页面原型参考dojo的,比网上jQuery的那些combox功能强,代码更小
2010/04/15 Javascript
自己做的模拟模态对话框实现代码
2012/05/23 Javascript
javascript禁用Tab键脚本实例
2013/11/22 Javascript
JavaScript中有关一个数组中最大值和最小值及它们的下表的输出的解决办法
2016/07/01 Javascript
如何利用ES6进行Promise封装总结
2019/02/11 Javascript
vue+element树组件 实现树懒加载的过程详解
2019/10/21 Javascript
python根据路径导入模块的方法
2014/09/30 Python
在Python的Flask框架下收发电子邮件的教程
2015/04/21 Python
python编写分类决策树的代码
2017/12/21 Python
pandas修改DataFrame列名的方法
2018/04/08 Python
用pandas中的DataFrame时选取行或列的方法
2018/07/11 Python
浅谈关于Python3中venv虚拟环境
2018/08/01 Python
Python中最大递归深度值的探讨
2019/03/05 Python
Python学习笔记之迭代器和生成器用法实例详解
2019/08/08 Python
django中间键重定向实例方法
2019/11/10 Python
tensorflow2.0与tensorflow1.0的性能区别介绍
2020/02/07 Python
Python sql注入 过滤字符串的非法字符实例
2020/04/03 Python
解决TensorFlow训练模型及保存数量限制的问题
2021/03/03 Python
CSS3新属性transition-property transform box-shadow实例学习
2013/06/06 HTML / CSS
英国儿童鞋和靴子:Start-Rite
2019/05/06 全球购物
Muziker英国:中欧最大的音乐家商店
2020/02/05 全球购物
汉米尔顿手表官网:Hamilton
2020/09/13 全球购物
《神奇的克隆》教学反思
2014/04/10 职场文书
怎样拟定创业计划书
2014/05/01 职场文书
年终工作总结范文2014
2014/11/27 职场文书
2014年社区工会工作总结
2014/12/18 职场文书
思想政治表现评语
2015/01/04 职场文书
补充协议书
2015/01/28 职场文书
2015年乡镇统计工作总结
2015/04/22 职场文书
2016公司中秋节寄语
2015/12/07 职场文书
Python 解决空列表.append() 输出为None的问题
2021/05/23 Python