前言
上次??途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ò)誤,歡迎指出。