理解好javascript的變數作用域和鏈式調用機制對用好變數起著關鍵的作用,本文來談談這兩個概念。 ...
理解好javascript的變數作用域和鏈式調用機制對用好變數起著關鍵的作用,下麵我來談談這兩個概念的理解。 (1)鏈式調用機制 作用域鏈的定義:函數在調用參數時會從函數內部到函數外部逐個”搜索“參數,一直找到參數為止,如果沒有聲明就返回null,聲明瞭沒有賦值就返回undefined,就像沿著一條鏈子一樣去搜索,這就是作用域的鏈式調用。 javascrip的變數作用域跟python或者其他後端語言不同,變數一經聲明,它的作用域就是全局的。在函數內部如果調用一個變數,就會發生上述的作用域鏈式調用的過程。 例子:
window.onload = function(){
var foo = true;
if(1==1){
var bar = foo*2;
console.log(bar);
}
console.log(foo);
}
列印的結果是:
2
true
這段代碼的實現過程大體是這樣的:
首先在if語句外部聲明一個foo變數並給它定義賦值,在if語句內部找不到foo,就會到全局作用域去尋找foo變數。而if語句內部並沒有改變foo的值,所以在外部列印foo時,它的值還是true。
如果像下麵一樣稍微修改一下代碼
window.onload = function(){
var foo = true;
if(1==1){
var foo = 2;
bar = foo*2;
console.log(foo);
console.log(bar);
}
console.log(foo);
}
列印結果是:
2
4
2
if語句內部聲明的變數foo把在if語句外面聲明的變數foo覆蓋了。
所以在聲明和引用變數的時候需要格外謹慎,一不小心,變數的值就改變了。
在ES6之前,要防止變數被污染,要使用閉包這個概念。
ES6為瞭解決這個問題,提出了兩種聲明變數的方法
let關鍵字和const關鍵字
1)let關鍵字
可以將變數綁定到所在的作用域中,通常是{ ... }內部。
例如:
window.onload = function(){
var foo = true;
if (1==1){
let foo = 2;
var bar = foo*2;
console.log(bar);
}
console.log(foo)
}
列印的結果是:
4
true
顯然,if語句內部聲明的foo並沒有影響到外部的foo,在if語句外部調用foo,還是原來的值true。
2)const關鍵字
const關鍵字同樣是用來創建塊作用域變數的,但其值時固定不變的。
(2)js中的特殊情況:作用域鏈的改變
以下語句或方法都會產生作用域鏈的改變
1)with(實參){ } 語句
2)try{ } catch(err){ } 語句
2)eval()方法
函數調用參數時都不會先執行函數內部的參數,而是調用此前已經定義過的參數,及函數被傳遞進來的實參。如果沒用實參的相關屬性值沒有定義過,再調用函數內部的參數屬性,即所謂的臨時改變。(catch內部的err比較特殊,有優先調用的權力)
(3)塊作用域的理解
塊作用域的定義:函數內部的參數只能在函數/語句內部使用,函數/語句塊外部不能使用,很多情況下塊作用域是隱式的,即錶面上看不出來。
跟全局變數不同,塊作用域內的變數不會鏈式調用。
塊作用域舉例:
1)for(var i)迴圈內部定義的參數i。在for迴圈結束後就會被銷毀
2)try{ } catch(err){ }語句內部的err對象。err只在catch內部調用,一旦函數執行完畢,馬上銷毀,即使函數外部想調用或者重新定義err也是無法調用到catch內部的err的
3)with(var i)內部新定義的參數i