JS在函數(shù)中形參和局部變量同名的問(wèn)題

前言

上次??途W(wǎng)做到這樣一個(gè)題,非常有意思,陷阱非常多,當(dāng)時(shí)覺(jué)得搞明白了,現(xiàn)在再看到,又糊涂了,發(fā)現(xiàn)了新的點(diǎn),看一下:

var foo={n:1};

(function (foo) {
    console.log(foo.n);
    foo.n = 3;
    var foo = {n:2};
    console.log(foo.n);
})(foo);

console.log(foo.n);

乍一看,是局部和全局變量的一些區(qū)分問(wèn)題,其實(shí)坑很多,下面我們一點(diǎn)點(diǎn)分析下;

變量的定義(宣告)和賦值

首先我們看一段代碼:

var a = 1;
var a;
console.log(a);//1

這里第二行對(duì)a是一個(gè)重復(fù)宣告,而不是賦值,變量只有定義(宣告)后未賦值的情況下才會(huì)輸出undefined,除非手動(dòng)賦值undefined;那么這里,JS引擎對(duì)于重復(fù)宣告的規(guī)定以最近的變量指定(也就是賦值)作為變量在執(zhí)行時(shí)的值,所以第二行的var a;其實(shí)相當(dāng)于無(wú)效;

函數(shù)中形參和局部變量同名

在我們自己寫代碼時(shí),一般不會(huì)做這種蠢事情,把形參和局部變量定義為同名,可如果真的這樣做了呢?
那就要分析下JS執(zhí)行上下文中的變量對(duì)象了,這個(gè)知識(shí)點(diǎn)不牢固的同學(xué)可以移步這里:重拾ECMAScript基礎(chǔ)——變量、作用域;
作用域鏈對(duì)變量的保存都是在變量對(duì)象中的,那么ES5對(duì)形參在變量對(duì)象中是如何保存的呢,請(qǐng)看規(guī)范:

10.5 Declaration Binding Instantiation
Every execution context has an associated VariableEnvironment. Variables and functions declared in ECMAScript code evaluated in an execution context are added as bindings in that VariableEnvironment‘s Environment Record. For function code, parameters are also added as bindings to that Environment Record.

就是說(shuō),無(wú)論是形參還是函數(shù)中聲明的變量,JS對(duì)他們的處理是沒(méi)有區(qū)別的,都是保存在這個(gè)函數(shù)的變量對(duì)象中作為局部變量進(jìn)行處理;那么結(jié)合上面我們說(shuō)到的變量的重復(fù)宣告,接下來(lái)同名的問(wèn)題就很簡(jiǎn)單了,看代碼:

(function fun (param) {
    var param;
    console.log(param);//1
    param = 2;
    console.log(param);//2
})(1);

在這里,同名的局部變量和形參其實(shí)是同一個(gè)東西,都是在函數(shù)的變量對(duì)象里的保存的那個(gè)變量;

如果變量是引用類型呢?

那么如果變量是個(gè)對(duì)象的話,就是我們文章一開(kāi)始提到的題目了,下面我們分析下:

var foo = {n : 1};
(function(foo) {
    console.log(foo.n);
    foo.n = 3;
    var foo = {n : 2};
    console.log(foo.n);
})(foo);
console.log(foo.n);

var foo = {n : 1};
function fun(foo) {
    var foo;
    console.log(foo.n);
    foo.n = 3;
    foo = {n : 2};
    console.log(foo.n);
};
fun(foo);
console.log(foo.n);

上下兩段代碼,意思是一樣的,我把匿名立即執(zhí)行函數(shù)換成了普通函數(shù)并在下一行調(diào)用,方便大家理解;

其實(shí)分析一下,就是這么幾個(gè)問(wèn)題;
內(nèi)部foo變量提升;
內(nèi)部foo和形參同名;
內(nèi)部foo重復(fù)宣告;
所以內(nèi)部var的那個(gè)foo和形參foo是同一個(gè)東西,并沒(méi)有發(fā)生變化;
然后重復(fù)宣告不影響之前的賦值,所以第一個(gè)為1;
接下來(lái),foo.n=3,由于形參為對(duì)象,所以是傳進(jìn)來(lái)的是一個(gè)對(duì)象的引用(指針);
對(duì)這塊知識(shí)點(diǎn)不牢固的同學(xué)還是請(qǐng)移步我之前那篇文章;
那么這個(gè)引用指向的堆內(nèi)存的那塊空間里的n改變?yōu)?;
接下來(lái),foo={n:2};這個(gè)就很有意思了,我們剛才也說(shuō)了,傳進(jìn)來(lái)的是個(gè)引用;
現(xiàn)在給這個(gè)引用賦值,實(shí)際上就是讓它指向新開(kāi)辟的空間,存放著{n:2}這個(gè)對(duì)象;
那么之前的引用就斷掉了,也就是說(shuō)形參foo已經(jīng)不指向全局里那個(gè)foo指向的空間了;
固然,在函數(shù)里,會(huì)輸出新空間里的2;
而在函數(shù)外,舊空間仍然沒(méi)有改變,故為3;

總結(jié)

其實(shí)這個(gè)題目考了很多知識(shí)點(diǎn),最后就是參數(shù)傳遞中引用類型的用法,這個(gè)也是ECMAScript中基礎(chǔ)的一個(gè)難點(diǎn),結(jié)合前面的一些,變量提升,重復(fù)宣告,形參與局部變量同名,算是解釋清楚了。

若有錯(cuò)誤,歡迎指出。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • __block和__weak修飾符的區(qū)別其實(shí)是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,591評(píng)論 0 6
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,665評(píng)論 1 51
  • ———————————————回答好下面的足夠了---------------------------------...
    恒愛(ài)DE問(wèn)候閱讀 1,842評(píng)論 0 4
  • 在js中,函數(shù)本身屬于對(duì)象的一種,因此可以定義、賦值,作為對(duì)象的屬性或者成為其他函數(shù)的參數(shù)。函數(shù)名只是函數(shù)這個(gè)對(duì)象...
    bjhu電net閱讀 608評(píng)論 0 5
  • 新年越發(fā)的近了 我回頭望了望自己過(guò)去的路 有坎坷、有艱辛 有鮮花掌聲,也有跌宕起伏 但是,我都挺過(guò)了 每個(gè)腳印里都...
    蕭娜閱讀 456評(píng)論 5 6

友情鏈接更多精彩內(nèi)容