作用域
JavaScript中的作用域分為全域性作用域,函式作用域,塊作用域。
全域性作用域
對於全域性範圍內宣告的變數,可以在任何地方訪問。
var messge = 'Hello'
function say(){
console.log(message)
}
say(); // Hello
函式作用域
在函式內宣告的變數,只能從該函式內部訪問。
function say() {
var messge = 'Hello';
console.log(messge);
}
say(); // Hello
console.log(greeting); // ReferenceError: messge is not defined
塊作用域
ES6 引入了let
和const
關鍵字,它們宣告的變數只能從該程式碼塊內訪問。
{
let messge = 'Hello';
console.log(messge);
}
console.log(messge); // ReferenceError: messge is not defined
靜態作用域
在詞法分析時(編譯時)確定。
let number = 42;
function printNumber() {
console.log(number);
}
function log() {
let number = 54;
printNumber();
}
log(); // 42
作用域鏈
JavaScript執行時,引擎首先會在當前範圍內查詢變數,如果找不到,會向父作用域查一層一層向上查詢,一直找到頂層全域性作用域,如果還是找不到就返回undefined
。
var g = 'Global hello'
function f1() {
var g1 = 'G1 hello';
function f2() {
var g2 = 'G2 hello'
function f3() {
console.log(g, g1, g2)
}
f3()
}
f2();
}
f1(); // Global hello G1 hello G2 hello
執行分析
執行上下文
執行上下文是執行一段 JavaScript 的環境,它儲存了執行程式碼的一些必要資訊。執行上下文分全域性執行上下文和函式執行上下文。
- 全域性執行上下文,一個程式中只會有一個,函式之外的程式碼都在全域性執行上下文中執行。
- 函式執行上下文,函式在每次呼叫時都會建立一個對應的函式執行上下文。
執行上下文的包含變數環境(Variable environment
),作用域鏈(Scope chain
),this
指向。
- 變數環境,函式內部所有的變數和物件引用和呼叫引數。
- 作用域鏈,當前函式之外的變數的引用組成。
this
指向。
從JavaScript執行時的記憶體結構來看,呼叫棧就是儲存執行上下文的集合。
案例
案例一
這是一個普通的JavaScript例子,執行流程分析:
var msg = 'Global hello'
function say() {
var msg = 'Hello'
return msg;
}
var s = say();
console.log(message) // Hello
- 建立全域性執行上下文,然後入呼叫棧。
- 呼叫函式
say()
,建立say()
的函式執行上下文,併入呼叫棧。 - 執行完
say()
函式將結果返回,更新全域性執行上下文裡的s
變數。 - 將
say
函式的執行上下文彈出棧。
案例二
這個例子跟上面不同的是返回值是函式,這個匿名函式也稱閉包,它訪問了函式外部的變數,即使外部函式執行上下文被彈出棧後,它依然可以持有外部變數的引用。執行分析如下:
var msg = 'Global hello'
function say() {
var msg = 'Hello'
return function() {
console.log(msg)
};
}
var s = say();
s()
- 建立全域性執行上下文,然後入呼叫棧。
- 呼叫函式
say()
,建立say()
的函式執行上下文,併入呼叫棧。 - 執行完
say()
函式將結果返回,更新全域性執行上下文裡的s
變數,s
是函式型別,它依然持有say
var = msg
引用。 - 將
say
函式的執行上下文彈出棧。 - 執行
s()
,建立s()
的函式執行上下文。 - 將
s()
函式的執行上下文彈出棧。
思考題
在腦海裡動態執行下面程式碼。
題目一:
var msg = 'Global hello'
function getMsgFunc() {
var msg = 'Hello';
function getMsg() {
return msg;
}
return getMsg();
}
console.log(getMsgFunc());
題目二:
var msg = 'Global hello'
function getMsgFunc() {
var msg = 'Hello';
function getMsg() {
return msg;
}
return getMsg;
}
console.log(getMsgFunc()());
題目三:
var msg = 'Global hello'
var obj = {
msg : 'Hello',
getMsgFunc : function(){
return function(){
return this.msg;
};
}
};
console.log(obj.getMsgFunc()());
題目四:
var msg = 'Global hello'
var obj = {
msg : 'Hello',
getMsgFunc : function(){
var that = this
return function(){
return that.msg;
};
}
};
console.log(obj.getMsgFunc()());