Javascript中函數的形參和函數內局部變量同名的問題

前言
上次牛客網作到這樣一個題,很是有意思,陷阱很是多,當時以爲搞明白了,如今再看到,又糊塗了,發現了新的點,看一下:函數

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);

乍一看,是局部和全局變量的一些區分問題,其實坑不少,下面咱們一點點分析下;lua

變量的定義(宣告)和賦值
首先咱們看一段代碼:指針

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

這裏第二行對a是一個重複宣告,而不是賦值,變量只有定義(宣告)後未賦值的狀況下才會輸出undefined,除非手動賦值undefined;那麼這裏,JS引擎對於重複宣告的規定以最近的變量指定(也就是賦值)做爲變量在執行時的值,因此第二行的var a;其實至關於無效;code

函數中形參和局部變量同名
在咱們本身寫代碼時,通常不會作這種蠢事情,把形參和局部變量定義爲同名,可若是真的這樣作了呢?
那就要分析下JS執行上下文中的變量對象了,這個知識點不牢固的同窗能夠移步這裏:重拾ECMAScript基礎——變量、做用域;
做用域鏈對變量的保存都是在變量對象中的,那麼ES5對形參在變量對象中是如何保存的呢,請看規範:對象

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.ip

就是說,不管是形參仍是函數中聲明的變量,JS對他們的處理是沒有區別的,都是保存在這個函數的變量對象中做爲局部變量進行處理;那麼結合上面咱們說到的變量的重複宣告,接下來同名的問題就很簡單了,看代碼:內存

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

在這裏,同名的局部變量和形參實際上是同一個東西,都是在函數的變量對象裏的保存的那個變量;ci

若是變量是引用類型呢?
那麼若是變量是個對象的話,就是咱們文章一開始提到的題目了,下面咱們分析下:作用域

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);

上下兩段代碼,意思是同樣的,我把匿名當即執行函數換成了普通函數並在下一行調用,方便你們理解;io

其實分析一下,就是這麼幾個問題;
內部foo變量提高;
內部foo和形參同名;
內部foo重複宣告;
因此內部var的那個foo和形參foo是同一個東西,並無發生變化;
而後重複宣告不影響以前的賦值,因此第一個爲1;
接下來,foo.n=3,因爲形參爲對象,因此是傳進來的是一個對象的引用(指針);
對這塊知識點不牢固的同窗仍是請移步我以前那篇文章;
那麼這個引用指向的堆內存的那塊空間裏的n改變爲3;
接下來,foo={n:2};這個就頗有意思了,咱們剛纔也說了,傳進來的是個引用;
如今給這個引用賦值,實際上就是讓它指向新開闢的空間,存放着{n:2}這個對象;
那麼以前的引用就斷掉了,也就是說形參foo已經不指向全局裏那個foo指向的空間了;
當然,在函數裏,會輸出新空間裏的2;
而在函數外,舊空間仍然沒有改變,故爲3;

總結
其實這個題目考了不少知識點,最後就是參數傳遞中引用類型的用法,這個也是ECMAScript中基礎的一個難點,結合前面的一些,變量提高,重複宣告,形參與局部變量同名,算是解釋清楚了。

如有錯誤,歡迎指出。

相關文章
相關標籤/搜索