网络编程
位置:首页>> 网络编程>> JavaScript>> javascript管中窥豹 形参与实参浅析

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

  发布时间:2024-04-16 09:25:54 

标签:形参,实参

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


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小西山子

0
投稿

猜你喜欢

  • 引言最近在工作中写一个批处理脚本,令人抓狂的是每次都不知道脚本要跑到啥时候结束,于是想到给程序添加个进度条。逛了一圈,没找到特别趁手的轮子,
  • 分割单词将一个标识符分割成若干单词存进列表,便于后续命名法的转换先引入正则表达式包import re至于如何分割单词看个人喜好,如以常见分隔
  • argparse是python用于解析命令行参数和选项的标准模块。很多时候,需要用到解析命令行参数的程序,目的是在终端窗口输入训练的参数和选
  • Win8.1下安装Python3.6提示0x80240017错误,如何解决?定位原因缺少Windows补丁KB2999226解决方法到Mic
  • 快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用。该方法的基本思想是:1.先从数列中取出一个数作为基准数
  • 最近,Analysis with Programming加入了Planet Python。作为该网站的首批特约博客,我这里来分享一下如何通过
  • 这个项目的名称与其叫做万能的XML不如叫做自动构建网站,根据一份XML文件,生成对应目录结构的网站,不过只有html还是太过于简单了,如果要
  • 操作步骤1.下载BeautifulReport文件,本例文件下载地址 最新文件下载地址2.复制文件BeautifulReport,至pyth
  • 引言一般来说,很多时候我们面临这样一种情况,即我们需要运行时间记录器,它不断向我们显示当前时间或在给定的时间间隔内保持执行一定的代码和平,在
  • Mint UI 是饿了么开源的,基于 Vue.js 的移动端组件库。关于Mint UI,有文档不够准确详尽,组件略显粗糙,功能不够完善等问题
  • 1.交换变量x = 6y = 5x, y = y, xprint x>>> 5print y>>> 62
  • 本文研究的主要是Python中optparser库的相关内容,具体如下。一直以来对optparser不是特别的理解,今天就狠下心,静下心研究
  • 本文实例为大家分享了微信小程序实现登陆注册滑块验证的具体代码,供大家参考,具体内容如下一、创建自定义组件MoveVerifyMoveVeri
  • 在开始部分,请看官非常非常耐心地阅读下面几个枯燥的术语解释,本来这不符合本教程的风格,但是,请看官谅解,因为列位将来一定要阅读枯燥的东西的。
  • 在编写python函数时,无意中发现一个问题:python中的变量不能以数字打头,以下函数中定义了一个变量3_num_varchar,执行时
  • 更换域名,估计是很多站长都很头疼的事情。通常大家的做法就是把老的域名通过IIS设置做301跳转到新的域名。对于只有一个虚拟空间的站长来说,换
  • 本文研究的主要是python处理csv数据动态显示曲线,分享了实现代码,具体如下。代码:# -*- coding: utf-8 -*- &q
  • 集合特点:集合对象是一组无序排列的可哈希的值:集合成员可以做字典的键,与列表和元组不同,集合无法通过数字进行索引。此外,集合中的元素不能重复
  • 我们在使用其他语言的库做编码转换时,对于无法理解的字符,通常的处理也只有两种(或三种):抛异常替换成替代字符跳过但是在复杂的现实世界中,由于
  • 在上篇博客中,提到了对一个脚本进行的多次优化。当时以为已经优化得差不多了,但是当测试人员测试时,我才发现,踩到了Python的一个大坑。在上
手机版 网络编程 asp之家 www.aspxhome.com