理解好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