作用域
定義:作用域是指程序源代碼中定義變量的區(qū)域。
作用:作用域規(guī)定了如何查找變量,也就是確定當前執(zhí)行代碼對變量的訪問權(quán)限。
在javaScript中的應用 :JavaScript采用詞法作用域(lexical scoping),也就是靜態(tài)作用域。
那什么又是 詞法作用域或者靜態(tài)作用域呢卦尊?
請繼續(xù)往下看
靜態(tài)作用域與動態(tài)作用域
因為javaScript采用的是詞法作用域(也就是靜態(tài)作用域),函數(shù)的作用域在函數(shù)定義的時候就決定了。
而詞法作用域相對的是動態(tài)作用域,函數(shù)的作用域是在函數(shù)調(diào)用的時候才決定的舌厨。
讓我們看一個例子來理解詞法作用域和動態(tài)作用域之間的區(qū)別:
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
// 結(jié)果是 ???
上面的代碼中:
1.我們首先定義了一個value岂却,并賦值為1;
2.聲明一個函數(shù)foo裙椭,函數(shù)的功能是打印 value 這個變量的值;
3.聲明一個函數(shù)bar躏哩,函數(shù)內(nèi)部重新創(chuàng)建了一個變量 value 這個變量賦值為2;
在函數(shù)內(nèi)部執(zhí)行了 foo() 這個函數(shù)揉燃;
4.執(zhí)行 bar() 這個函數(shù)
假設javaScript采用靜態(tài)作用域震庭,讓我們分析下執(zhí)行過程:
執(zhí)行foo函數(shù),首先從 foo 函數(shù)內(nèi)部查找是否有變量 value ,如果沒有
就根據(jù)書寫的位置,查找上面一層的代碼你雌,我們發(fā)現(xiàn)value等于1器联,所以結(jié)果會打印 1二汛。
假設javaScript采用動態(tài)作用域,讓我們分析下執(zhí)行過程:
執(zhí)行foo函數(shù)拨拓,依然是從 foo 函數(shù)內(nèi)部查找是否有局部變量 value肴颊。如果沒有,
就從調(diào)用函數(shù)的作用域渣磷,也就是 bar 函數(shù)內(nèi)部查找 value 變量婿着,所以結(jié)果會打印 2。
上面在區(qū)分靜態(tài)作用于和動態(tài)作用域的時候,我們已經(jīng)說了如果是靜態(tài)作用域醋界,那么函數(shù)在書寫定義的時候已經(jīng)確定了竟宋,而動態(tài)作用域是函數(shù)執(zhí)行過程中才確定的。
JavaScript采用的是靜態(tài)作用域形纺,所以這個例子的結(jié)果是 1丘侠。
我們在控制臺中輸入執(zhí)行上面的函數(shù),檢驗一下執(zhí)行結(jié)果果然是 1逐样。
動態(tài)作用域
那什么語言是采用的動態(tài)的作用域呢? 其實bash 就是動態(tài)作用域蜗字,
我們可以新建一個 scope.bash 文件將下列代碼放進去,執(zhí)行一下這個腳本文件:
#!/bin/bash
value=1
function foo () {
echo $value;
}
function bar () {
local value=2;
foo;
}
bar
上面代碼運行的結(jié)果輸出2很好解釋脂新,雖然在代碼最上層定義了 value并賦值為1挪捕,但是在調(diào)用foo函數(shù)的時候,在查找 foo 內(nèi)部沒有 value 變量后,會在foo 函數(shù)執(zhí)行的環(huán)境中繼續(xù)查找争便,也就是在bar 函數(shù)中查找级零,很幸運我們找到了。~
看一道題
var scope = "global scope";
function checkscope(){
var scope = "local scope";
return scope;
}
console.log(checkscope());//local scope
原因是:函數(shù)內(nèi)的變量優(yōu)先級高于全局變量滞乙,如果在函數(shù)內(nèi)聲明的局部變量和全局變量重名奏纪,那么在函數(shù)局部范圍內(nèi),局部變量的賦值和計算將頂替全局變量原有的值酷宵,但是在函數(shù)之外繼續(xù)引用全局變量亥贸,全局變量原來的值不變躬窜。
很容易理解
再直接打印一下scope,可以看到是沒有改變的
var scope = "global scope";
function checkscope(){
var scope = "local scope";
return scope;
}
console.log(checkscope());
console.log(scope);//global scope
這個時候把var去掉,全局scope被覆蓋了
var scope = "global scope";
function checkscope(){
scope = "local scope"; //去掉了var聲明
return scope;
}
console.log(checkscope());
console.log(scope);//從global scope變成了local scope
再來看看當函數(shù)內(nèi)嵌套多層的情況:
var scope = ' global scopr'; // 全局變量 現(xiàn)在值時global scope
function checkscope(){ // 第一層函數(shù)
var scope = 'local scope'; //創(chuàng)建了一個函數(shù)內(nèi)的全局變量
function nested(){
var scope = 'nested scope'; //創(chuàng)建了一個函數(shù)內(nèi)的全局變量
return scope; //這里放回 socope 的結(jié)果浇垦,是netsed scope
}
return nested(); //返回netstd的值 也是 netsed scope
}
console.log(checkscope())// 所以這里的值是 nested scope
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
在checkscope 函數(shù)中 又定義一個函數(shù) f ,這個函數(shù) 只做了一件事:返回scope 這個變量;
最后返回并執(zhí)行 f 這個函數(shù)荣挨;
調(diào)用checkscope
按照javaScript中靜態(tài)作用域理解男韧,在執(zhí)行 checkscope 這個函數(shù)的時候在函數(shù)內(nèi)部執(zhí)行的是f 這個函數(shù),首先在 f 這個函數(shù)內(nèi)部查找 scope 這個變量發(fā)現(xiàn)沒有默垄,繼續(xù)在定義函數(shù)f的上面一層查找此虑,發(fā)現(xiàn)在checkscope 這個函數(shù)作用域內(nèi) 找到了scope的值 直接返回,至于 checkscope外面定義的scope沒有理睬口锭。
再舉一個栗子
// 例2:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
單純的返回 f 這個函數(shù)朦前;
調(diào)用checkscope
按照我們上面解釋的javaScript中靜態(tài)作用域理解介杆,在執(zhí)行 checkscope 這個函數(shù)的時候在函數(shù)內(nèi)返回了函數(shù)f實際是在最外面調(diào)用的f但是由于javaScript是采用的詞法作用域,因此函數(shù)的作用域基于函數(shù)創(chuàng)建的位置韭寸。
而引用《JavaScript權(quán)威指南》的回答就是:
JavaScript 函數(shù)的執(zhí)行用到了作用域鏈春哨,這個作用域鏈是在函數(shù)定義的時候創(chuàng)建的。嵌套的函數(shù) f() 定義在這個作用域鏈里恩伺,其中的變量 scope 一定是局部變量赴背,不管何時何地執(zhí)行函數(shù) f(),這種綁定在執(zhí)行 f() 時依然有效晶渠。
但是在這里真正想讓大家思考的是:
雖然兩段代碼執(zhí)行的結(jié)果一樣凰荚,但是兩段代碼究竟有哪些不同呢?