以前錯誤的認為,全局變量名、局部變量名和形參名相同時,全局變量(也就是外部變量)被形參覆蓋,形參被局部變量覆蓋。
今天發現這樣理解並不對。比如
function foo(num){ var num; console.log(num); } foo(1) // 1 //如果錯誤的理解為局部變量會覆蓋形參的話,會認為會輸出undefined
那事實是什么呢。實際上變量名沖突分兩種,一種是函數外的變量和函數里的局部變量的沖突,一種是函數內部的沖突。
第一種沖突,我把它理解為是作用域鏈的上游(最上游是全局對象的命名空間)會被下游函數的局部變量覆蓋。
其實我覺得這就是一種繼承關系,好比原型鏈里離目標對象近的新的方法覆蓋遠的舊的方法,甚至是好比HTML/CSS里字體、顏色等屬性的繼承。
只不過這里繼承的是一個上下文環境。
它們都有一個特點,自己有,用自己的;自己沒有,用父級的;父級沒有,再逐級向上。
//和HTML/CSS屬性繼承不同的是,JS作用域鏈還涉及變量/函數聲明 //簡單說,就是子元素一旦聲明了變量,不管有沒有賦值,都算做是有自己的了 //也就不會操作到父級了 var b; function foo2() { console.log(b); var b; } foo2();//undefined
第二中是函數內部的沖突,即同一作用域內的沖突。
形參和局部變量其實就是同一作用域的。只不過JS里的形參省略了聲明(像是Java就需要和變量一樣做類型聲明)。
而在JS里有:同一作用域,重復申明但不賦值,不會對變量有影響
var a=1; console.log(a); //1 var a; console.log(a); //1
這也就解釋了,為什么局部變量申明但不賦值,不影響形參。
或許你會有我之前的另一種猜測,局部變量聲明但不賦值,不會覆蓋形參,但賦值了不是改變形參,而是覆蓋。
那我們再看看這個
var b; function foo2(b) { var b=2; console.log(b); console.log(arguments[0]); } foo2(1); //假如是局部變量名覆蓋形參名的話,那應該打印'2 1' //而事實是'2 2' //所以其實局部變量和形參沖突,就是重復定義個變量 //所以我覺得其實可以把形參理解為一個特殊的局部變量 //只不過它在函數定義時被申明,而且申明的var 被省略 function foo2(var b){ }
