javascript管中窺豹 形參與實(shí)參淺析
今天看到別人的一個(gè)題目:
function fn(x){
x = 10;
arguments[0] = 20;
console.log(x,arguments[0])
}
fn()
感覺(jué)自己對(duì)這也是一知半解,自己也可以試一下,于是就特地分析一下。
本想從語(yǔ)言的角度來(lái)分析,無(wú)奈功力不夠,只能粗淺的嘗試一下,于是稱(chēng)之管中窺豹,還望大牛指正。
這是昨天寫(xiě)的,今天吃飯的時(shí)候又想了一下,想來(lái)想去感覺(jué)有些問(wèn)題還是說(shuō)得不靠譜,于是又試著修改了一下。
每一本js入門(mén)書(shū)籍都會(huì)提到,JS的函數(shù)內(nèi)部有一個(gè)Arguments的對(duì)象arguments,用來(lái)函數(shù)調(diào)用的時(shí)候?qū)嶋H傳入函數(shù)的參數(shù),fn.length保存形參的長(zhǎng)度。
這些對(duì)分析來(lái)說(shuō)略有用處,可是我想得到更多形參的信息,不知道有誰(shuí)有比較好的辦法,我暫時(shí)無(wú)解。
于是只能模擬了。
先不理會(huì)模擬,從實(shí)際問(wèn)題出發(fā):
<!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
重點(diǎn)關(guān)注后面兩個(gè)函數(shù)(fn_1,fn_2)的執(zhí)行,在這里,我們直接重新聲明了形參對(duì)應(yīng)的x,看到網(wǎng)上有的人說(shuō)這是聲明的一個(gè)局部變量x。
也是,不過(guò)這個(gè)局部變量不是一般的局部變量,x直接關(guān)聯(lián)對(duì)應(yīng)的arguments,上面的實(shí)例中就是x關(guān)聯(lián)arguments[0];
所以我猜測(cè)這個(gè)賦值的流程應(yīng)該是
1、函數(shù)定義的時(shí)候,聲明了形參,如果函數(shù)體內(nèi)有相同名稱(chēng)的局部變量,則忽略此聲明。同時(shí)函數(shù)體內(nèi)同時(shí)會(huì)有一個(gè)對(duì)象arguments;
(亂入一句:個(gè)人以為arguments當(dāng)初不定義成數(shù)組的一個(gè)考慮是否是因?yàn)樵诤瘮?shù)定義內(nèi)無(wú)法確定實(shí)際參數(shù)的個(gè)數(shù)[運(yùn)行時(shí)動(dòng)態(tài)確定],那么要么這個(gè)數(shù)組無(wú)限大,要么數(shù)組一取值就越界)。
回到正題:
對(duì)于fn_2,初始化形參相當(dāng)于var x;(此時(shí)x沒(méi)有賦值,默認(rèn)為undefined,賦值是在語(yǔ)句執(zhí)行的時(shí)候賦值的)
所以如果可以這么寫(xiě)的話(huà),fn_2就應(yīng)該是這樣:
function fn_2(var x){
x = 3;
console.log(x,arguments[0]);
arguments[0] = 2;
console.log(x,arguments[0]);
}
2、函數(shù)語(yǔ)法檢測(cè)通過(guò),執(zhí)行的時(shí)候,函數(shù)內(nèi)部的arguments對(duì)象一開(kāi)始就得到賦值,賦值完畢后,函數(shù)體內(nèi)的語(yǔ)句開(kāi)始執(zhí)行。
下面的一段表述是我自己想的,不知道正確不正確(特別是關(guān)聯(lián)的說(shuō)法):
一旦發(fā)現(xiàn)形參(對(duì)應(yīng)的變量)被賦值,那么會(huì)去尋找arguments對(duì)應(yīng)的項(xiàng),如果發(fā)現(xiàn)了arguments對(duì)應(yīng)的項(xiàng),那么設(shè)置形參與arguments對(duì)應(yīng)項(xiàng)的關(guān)聯(lián)。如果沒(méi)有發(fā)現(xiàn)arguments里面對(duì)應(yīng)的項(xiàng)(undefined),那么形參和arguments還是保持獨(dú)立。這里尋找的是arguments在函數(shù)運(yùn)行開(kāi)始的一個(gè)快照。反過(guò)來(lái)arguments賦值也是一樣。
上面的刪除的部分是昨天的,紅字部分是寫(xiě)到一半的時(shí)候發(fā)現(xiàn)有問(wèn)題加上去的。今天回過(guò)神來(lái),昨天為什么要傻逼的想到快照呢,這個(gè)不就是函數(shù)開(kāi)始運(yùn)行時(shí)直接
判斷關(guān)聯(lián)么?于是改了一下表述:
在函數(shù)開(kāi)始執(zhí)行時(shí),設(shè)置形參與arguments的關(guān)聯(lián)信息。如果形參與對(duì)應(yīng)的arguments里面能找到對(duì)應(yīng)的項(xiàng)(均為undefined),那么兩者關(guān)聯(lián)。后面不論怎么處理,都不會(huì)改變整個(gè)函數(shù)體內(nèi)的關(guān)聯(lián)信息。
于是后面的實(shí)例說(shuō)明的說(shuō)法也要改變:
回到例子,fn_2函數(shù)語(yǔ)法檢測(cè)通過(guò),從第二步開(kāi)始執(zhí)行:
不帶參數(shù)的情況
fn_2();
function fn_2(x){//arguments賦值完成,由于沒(méi)有實(shí)參,于是arguments參數(shù)列表為空。同時(shí)判斷關(guān)聯(lián)信息,顯然形參有,arguments空,兩者相互獨(dú)立,以后都不會(huì)再關(guān)聯(lián)
var x = 3;//x賦值為3,x與arguments[0]相互獨(dú)立,arguments[0]還是為undefined
console.log(x,arguments[0]);//打印x=3,arguments[0]為undefined
arguments[0] = 2;//arguments被賦值,x與arguments[0]相互獨(dú)立。因此x=3不改變
console.log(x,arguments[0]);//打印x = 3,arguments[0]=2
}
帶參數(shù)的情況
帶參數(shù)的情況 fn_2(1);
function fn_2(x){//arguments賦值完成,arguments[0]=1。同時(shí)形參x有值,兩者相關(guān)聯(lián),永結(jié)同心。
var x = 3;//x賦值為3,x與arguments[0]關(guān)聯(lián),于是arguments[0]被賦值為3,。
console.log(x,arguments[0]);//打印x=3,arguments[0] = 3
arguments[0] = 2;//arguments[0]被賦值2,由于x與arguments[0]已經(jīng)關(guān)聯(lián)到一起,于是x同時(shí)改變
console.log(x,arguments[0]);//打印x = 2,arguments[0]=2
}
反過(guò)來(lái)應(yīng)該也是一樣的:
不帶參數(shù)
fn_2();
function fn_2(x){//不關(guān)聯(lián)
arguments[0] = 2;//找不到對(duì)應(yīng)的x(undefined),相互獨(dú)立
console.log(x,arguments[0]);//undefined,2
x = 3;//相互獨(dú)立,快照。雖然arguments動(dòng)態(tài)添加了,老死不相往來(lái),所以依舊失敗
console.log(x,arguments[0]);//3,2
}
帶參數(shù)
fn_2(1);
function fn_2(x){
arguments[0] = 2;//關(guān)聯(lián)
console.log(x,arguments[0]);//2,2
x = 3;//關(guān)聯(lián)
console.log(x,arguments[0]);//3,3
}
由于我們只有一個(gè)形參,可能說(shuō)服力不夠,現(xiàn)在增加到兩個(gè)。
只有一個(gè)實(shí)參的情況:
fn_2(1);
function fn_2(x,y){ //arguments賦值完成,arguments[0]=1,arguments[1]=undefined,因此只有x與arguments[0]關(guān)聯(lián),y與arguments[1]老死不往來(lái)
console.log(x,y,arguments[0],arguments[1]); //1,undefined,1,undefined
var x = 3; //x賦值為3,x與arguments[0]關(guān)聯(lián),于是arguments[0]被賦值為3。
console.log(x,y,arguments[0],arguments[1]); //3,undefined,3,undefined
var y = 4; //y賦值為3,y與arguments[1]相互獨(dú)立,arguments[1]還是為undefined
console.log(x,y,arguments[0],arguments[1]); //3,4,3,undefined
arguments[0] = 2; //arguments[0]被賦值2,由于x與arguments[0]已經(jīng)關(guān)聯(lián)到一起,于是x同時(shí)改變
console.log(x,y,arguments[0],arguments[1]); //2,4,2,undefined
arguments[1] = 5; //arguments[1]被賦值5,y與arguments[1]相互獨(dú)立,于是y還是保持為4
console.log(x,y,arguments[0],arguments[1]); //x=2,y=4,arguments[0]=2,arguments[1]=5
}
有兩個(gè)實(shí)參的情況:
fn_3(1,6);
function fn_3(x,y){ //arguments賦值完成,arguments[0]=1,arguments[1]=6,x與arguments[0],y與arguments[1]都相互關(guān)聯(lián)
console.log(x,y,arguments[0],arguments[1]); //1,6,1,6
var x = 3; //x賦值為3,x與arguments[0]關(guān)聯(lián),于是arguments[0]被賦值為3。
console.log(x,y,arguments[0],arguments[1]); //3,6,3,6
var y = 4; //y賦值為3,y與arguments[1]關(guān)聯(lián),于是arguments[1]被賦值為4。
console.log(x,y,arguments[0],arguments[1]); //3,4,3,4
arguments[0] = 2; //arguments[0]被賦值2,由于x與arguments[0]已經(jīng)關(guān)聯(lián)到一起,于是x同時(shí)改變
console.log(x,y,arguments[0],arguments[1]); //2,4,2,4
arguments[1] = 5; //arguments[1]被賦值5,由于y與arguments[1]已經(jīng)關(guān)聯(lián)到一起,于是y同時(shí)改變
console.log(x,y,arguments[0],arguments[1]); //x=2,y=5,arguments[0]=2,arguments[1]=5
}
以上全部是推測(cè),因?yàn)閷?shí)際中沒(méi)有辦法形參的信息,所以我按照推測(cè)寫(xiě)了一個(gè)小測(cè)試:
下面的也改了:
function _Function(){//獲得的形參列表為數(shù)組:_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];
//下面的方法獲取實(shí)參_arguments,這里_arguments實(shí)現(xiàn)為一個(gè)數(shù)組,而非arguments對(duì)象
this.exec = function(){
//函數(shù)運(yùn)行時(shí),實(shí)參_arguments被賦值
var _arguments = [];
for(var i = 0; i < arguments.length; i++){
_arguments[i] = arguments[i];
}
//下面執(zhí)行函數(shù)體
eval(fn_body);
}
}
替換成:
function _Function(){//獲得的形參列表為數(shù)組:_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];
//下面的方法獲取實(shí)參_arguments,這里_arguments實(shí)現(xiàn)為一個(gè)數(shù)組,而非arguments對(duì)象
this.exec = function(){
//函數(shù)運(yùn)行時(shí),實(shí)參_arguments被賦值
var _arguments = [];
for(var i = 0; i < arguments.length; i++){
_arguments[i] = arguments[i];
}
//在運(yùn)行開(kāi)始就判斷關(guān)聯(lián)信息
for(var j = 0; j < Math.min(_arguments.length,_args.length); j++){
_args[j]["link"] = true;
}
//下面執(zhí)行函數(shù)體
eval(fn_body);
}
}
上面按理來(lái)說(shuō),關(guān)聯(lián)應(yīng)該是把兩者指向同一個(gè)對(duì)象,可是我只需要分析例子,沒(méi)打算做得那么精細(xì),所以是在函數(shù)體里面用if語(yǔ)句判斷的 。
把例子中fn_2換成對(duì)應(yīng)的形式就是:
// 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;來(lái)表示形參與實(shí)參相關(guān)聯(lián)
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);
畫(huà)了一張圖來(lái)表示實(shí)例與改寫(xiě)函數(shù)兩者的關(guān)系,順便也改了一下:

function fn(x){
x = 10;
arguments[0] = 20;
console.log(x,arguments[0])
}
fn()
顯然,兩者相互獨(dú)立:
x = 10,arguments[0] = 20;
推測(cè)一下:
function fn(x){
x = 10;
arguments[0] = 20;
console.log(x,arguments[0])
}
fn(1)
應(yīng)該都是輸出20,20
function fn(x){
arguments[0] = 20;
console.log(x,arguments[0])
}
fn(1)
應(yīng)該也都是輸出20,20
function fn(x){
arguments[0] = 20;
console.log(x,arguments[0])
}
fn()
應(yīng)該是undefined和20
原文來(lái)自cnblogs小西山子
相關(guān)文章
JavaScript創(chuàng)建對(duì)象的幾種方式及關(guān)于this指向問(wèn)題
這篇文章主要介紹了JavaScript創(chuàng)建對(duì)象的幾種方式及關(guān)于this指向問(wèn)題,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值。需要的小伙伴可以參考一下2022-07-07
javascript滾輪事件基礎(chǔ)實(shí)例講解(37)
這篇文章主要為大家詳細(xì)介紹了javascript滾輪事件基礎(chǔ)實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
javascript前端埋點(diǎn)上報(bào)的幾種方式
本文將介紹前端埋點(diǎn)上報(bào)的幾種常見(jiàn)方式,并詳細(xì)闡述如何在項(xiàng)目中運(yùn)用這些方式進(jìn)行數(shù)據(jù)上報(bào),以幫助開(kāi)發(fā)者更好地進(jìn)行數(shù)據(jù)收集和分析,感興趣的可以了解一下2023-11-11
js實(shí)現(xiàn)小球在頁(yè)面規(guī)定的區(qū)域運(yùn)動(dòng)
這篇文章主要為大家詳細(xì)介紹了js控制小球在規(guī)定范圍運(yùn)動(dòng),碰到邊界就改變運(yùn)動(dòng)方向,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06
uni-app使用uni-download和uni.saveFile下載保存文件遇到的問(wèn)題及解決方法
這篇文章主要給大家介紹了關(guān)于uni-app使用uni-download和uni.saveFile下載保存文件遇到的問(wèn)題及解決方法的相關(guān)資料,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下2024-01-01
JS中的6種打斷點(diǎn)的方式實(shí)例總結(jié)
斷點(diǎn)調(diào)試是啥?難不難? 斷點(diǎn)調(diào)試其實(shí)并不是多么復(fù)雜的一件事,簡(jiǎn)單的理解無(wú)外呼就是打開(kāi)瀏覽器,打開(kāi)sources找到j(luò)s文件,在行號(hào)上點(diǎn)一下罷了,下面這篇文章主要給大家介紹了關(guān)于JS中6種打斷點(diǎn)方式的相關(guān)資料,需要的朋友可以參考下2022-04-04
javascript下利用arguments實(shí)現(xiàn)string.format函數(shù)
sitepoint上看到Andrew Tetlaw在08年寫(xiě)的文章arguments: A JavaScript Oddity,閱讀之后,除了對(duì)arguments溫故知新一遍以外,印象最深刻的還是Andrew的第一個(gè)函數(shù)實(shí)現(xiàn)的string.format功能。2010-08-08

