作用域是一個基礎但又很重要的概念酵使,對于初學者來說,深入了解作用域是前端進階的基礎。
1. 什么是作用域
簡單的來說吭练,作用域就是程序源代碼中定義變量的區(qū)域,也是你代碼當前的上下文環(huán)境析显,更好的解釋就時內(nèi)存中開辟出來的一段空間鲫咽,JavaScript采用詞法作用域,同時也被稱作靜態(tài)作用域谷异。
靜態(tài)作用域分尸、動態(tài)作用域:
var a= 1;
function fun1() {
console.log(a);
}
function fun2() {
var a= 2;
fun1();
}
fun1(); // 1
由于JavaScript采用的是靜態(tài)作用域,所以他的執(zhí)行過程如下:
執(zhí)行 fun1函數(shù)歹嘹,在從 fun1函數(shù)內(nèi)部查找是否有局部變量 a箩绍。如果沒有,查找上面一層的代碼尺上,也就是 a等于 1材蛛,所以最終結(jié)果為 1。
那么JavaScript采用的是動態(tài)作用域呢怎抛,他的執(zhí)行是怎么的卑吭?
執(zhí)行 fun1函數(shù),從 fun1函數(shù)內(nèi)部查找是否有局部變量 a马绝。如果沒有豆赏,就從調(diào)用fun1的函數(shù)中查找是否有a,即fun2 函數(shù)中找a 變量迹淌,這樣執(zhí)行的結(jié)果為2河绽。
2. 作用域之間的差異
function a() {
function b() {
c = 1;
}
}
提出問題,c的作用域應該是唉窃?b的耙饰?a的?哦纹份,原來是全局的苟跪,因為前面沒有加var廷痘。
局部作用域:
一般只在固定的代碼片段內(nèi)可訪問到,如函數(shù)內(nèi)部
直接上代碼:
function fun1() {
var a = 1;
console.log(a);
}
fun1(); // 1
function fun2() {
var b = 2; // b只能在函數(shù)內(nèi)部使用
}
fun2();
console.log(b); // 報錯:b b is not defined
全局作用域:
var c = 3;
function fun3() {
console.log(c);
}
fun3(); // 3
function fun4() {
d = 4; //
}
fun4();
console.log(d); // 4
從上面兩個例子中可以看到:函數(shù)內(nèi)部可以訪問外部變量件已,函數(shù)外部不能訪問函數(shù)內(nèi)部變量笋额。
此處還應該注意:
如果是在過程外部,用var和不用var定義的變量都是全局變量篷扩,但是在過程內(nèi)部兄猩,用了var定義的變量是局部變量,沒有用var定義的變量就是全局變量鉴未。
3. ES6中新增的let作用域
在ES6未出現(xiàn)之前沒有塊級作用域的概念枢冤,這就會導致在一些情況下,局部變量常常會污染全局變铜秆,舉個例子:
var a = 1;
function fun1() {
var a = 2;
console.log(a);
}
fun1(); // 2 局部變量即fun1中的變量a淹真,替換了全局變量中的a,污染了全局變量连茧。
ES6中的塊級作用域核蘸,在{}睡蟋、for蒿柳、if中都可以聲明。
ES6有幾個特點:
1茂装、let是塊級變量坯汤,不存在于window下虐唠,window.變量名是找不到的
2、 在沒有聲明的情況下使用會拋出異常
3惰聂、也不允許重復聲明變量疆偿,即變量名唯一
let a = 1;
window.a; // undefined
function fun1(){
console.log(b);
let b =2;
}
fun1(); // 報錯 fun1 is not defined
function fun2() {
let c =2;
var c = 3;
console.log(c);
}
fun2(); // 報錯 Identifier 'b' has already been declared
let在for循環(huán)中的使用:
(先看一下var)
for(var i=0; i<2; i++){
console.log('outer i: ' + i);
for(var i=0; i<2; i++){
console.log('inner i: '+i); // 外層循環(huán)被打斷了,因為i為全局變量所以 i 的值被內(nèi)層循環(huán)修改了
}
}
結(jié)果是:
outer i: 0
inner i: 0
inner i: 1
把var換成let:
for(var i=0; i<2; i++){
console.log('outer i: ' + i);
for(let i=0; i<2; i++){
console.log('inner i: '+i); // 每一次循環(huán)都重新綁定一次作用域
}
}
結(jié)果是:
outer i: 0
outer i: 0
inner i: 0
inner i: 1
outer i: 1
inner i: 0
inner i: 1