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