前言
關于作用域的有關知識點有全域作用域、區域作用域、函式作用域、塊級作用域、詞法作用域、作用域鏈,
作用域
作用域就像是一個教室,上課時教室里面的人互相可見,A 教室里的人不可以看見 B 教室里的人,作用域決定了代碼生效的區域以及資源(變數、函式)可見的區域,
function fun() {
let a = 20;
}();
console.log(a); // Uncaught ReferenceError: a is not defined
無法獲得fun函式中定義的變數a,
全域作用域
全域作用域的范圍比其他的作用域的范圍更大,關系就像是一切 JavaScript 物件的頂層都是 Object,<script>或.js可以算作是一個全域作用域,定義在全域作用域的變數叫全域變數,
在全域作用域宣告的變數,其他的作用域都可以訪問:
let a = 20;
function fun() {
console.log(a) // => 20
}
fun();
區域作用域
定義在區域作用域里面的變數就是區域變數,區域變數只可以在區域作用域生效,區域作用域可以訪問到全域作用域的變數,或是比區域作用域大一點的父作用域(嵌套作用域),
區域作用域有塊級作用域、函式作用域,
塊級作用域
在 ES5 及以前,塊級作用域受var影響是無效的,具體請看ES6 關鍵字 let 和 ES5 及以前關鍵字 var 的區別,
{
var x = 10;
}
console.log(x) // => 10
for (var i = 0; i < 10; i++) {
// ...
}
console.log(i); // 10
最后列印for陳述句的變數 i,得到 10,實際上在陳述句內部列印最侄訓圈的結果是 9,ES6 之后的關鍵字let宣告的變數,外部想要使用塊級作用域的變數x就會報錯:
{
let x = 10;
}
console.log(x); // Uncaught ReferenceError: x is not defined
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i); // Uncaught ReferenceError: i is not defined
函式作用域
關于函式作用域,有一個面試題,需要結合下面的詞法作用域進行分析,
let a = 123;
function fun1() {
console.log(a);
}
function fun2() {
let a = 456;
fun1();
}
fun2();
最終的結果是 123,這是因為詞法作用域已經決定了函式fun1參考的外部作用域的變數是全域作用域中的變數 a,而非函式fun2定義的區域變數a,
詞法作用域
函式參考變數的靜態性
詞法作用域(靜態作用域)是一種就近原則,也就是在我們寫下代碼的時候就已經決定了函式參考的變數應該是按照就近原則來參考的:
詞法作用域的靜態性原則,規定函式參考變數必須按照代碼書寫的順序來,即便是函式被其他函式呼叫了,這個函式的作用域也不會發生變化,也不會因此變成了嵌套函式,
函式自身區域作用域內沒有定義變數 a,而在全域作用域中,定義了變數 a,根據就近原則,所以參考的是a = 123,
let a = 123;
function fun() {
console.log(a); // => 123
}
如果函式體內有一個變數 a,結果就是:
let a = 123;
function fun() {
let a = 456;
console.log(a); // => 456
}
總而言之,函式參考變數時是按照一種自上而下,順序來的,函式參考一個變數,前提是變數不能在函式宣告之后出現,
錯誤:
fun();
function fun() {
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
}
let a = 123;
正確:
let a = 123;
fun();
function fun() {
console.log(a); // 123
}
這里有一個懸念,為什么呼叫函式可以在函式宣告之前?
函式呼叫的動態性
函式要成功參考作用域外的變數必須是變數宣告在函式之前,但是函式呼叫可以在函式宣告之前,但也必須是在變數宣告之后,
在函式呼叫時體現出作用域的動態性,函式參考變數就體現出作用域的靜態性,
function f() { g(); }
function g() {}
f();
當我們呼叫 f(),它會呼叫 g(),在執行期間,g 被 f 呼叫代表了一種動態的關系,
作用域鏈
當在函式使用一個變數的時候,首先 Javascript 會嘗試在當前作用域下去尋找該變數,如果沒找到,再到它的上層作用域尋找,以此類推直到找到該變數或是已經到了全域作用域,
如果在全域作用域里仍然找不到該變數,它就會在全域范圍內隱式宣告該變數(非嚴格模式下)或是直接報錯,

把作用域比喻成一個建筑,這份建筑代表程式中的嵌套作用域鏈,第一層代表當前的執行作用域,頂層代表全域作用域,
根據詞法作用域靜態性的原則,函式參考變數不會因為呼叫順序和位置從而改變當前的作用域,所以查找變數的位置按照代碼書寫的位置來看,
面試題
現在可以回答那一道面試題了函式作用域:
let a = 123;
function fun1() {
console.log(a);
}
function fun2() {
let a = 456;
fun1();
}
fun2();
函式fun2宣告了一個與全域作用域同名的變數 a,根據詞法作用域的靜態性原則,函式呼叫不會改變函式的作用域,函式fun1作用域內沒有定義變數a,它的上級作用域就是全域作用域,而全域作用域中宣告了變數 a,所以最終列印結果是 123,
參考文獻
- 《JavaScript 權威指南》- 第 3 章 變數作用域;
- 《深入理解 JavaScript》- 第 16 章 變數:作用域、環境和閉包;
- web前端面試 - 面試官系列 - 面試官:說說你對作用域鏈的理解,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/503487.html
標籤:其他
上一篇:JavaScript學習
下一篇:# JavaScript 物件
