十萬個Web前端面試題之作用域與上下文

本來想寫閉包的,寫著寫著,突然想到閉包是和作用域相關的,那應該先寫個作用域的。

概念

作用域,就是指在一個變數或函式,可以在指定的範圍內被執行,這個範圍就叫做這個變數或函式的作用域。

語言的作用域通常分為靜態作用域和動態作用域

靜態作用域:靜態作用域是指在程式碼在定義時,透過語法分析階段就確定了,不會改變。

動態作用域:動態作用域是在執行時根據程式的流程資訊來動態確定的,而不是在寫程式碼時進行靜態確定的。

JavaScript是屬於靜態作用域。為什麼?我們來看一個非常經典的作用域測驗:

var animal = ‘is cat’

function cat() {

console。log(animal)

}

function dog() {

var animal= ‘is dog’

cat()

}

dog()

// 結果是啥呢?

大家可以在瀏覽器裡面跑一下,上面的結果是is cat,怎麼理解呢?

如果它是靜態作用域

,它執行dog()方法後,進入裡面執行cat()方法,在cat方法裡面,列印animal時,它先在cat作用域裡面找animal,發現沒有定義,就直接去全域性作用域裡面找animal了,於是列印is cat

如果它是動態作用域名

,在cat方法執行時沒找到animal的話,會從呼叫函式的作用域查詢,也就是dog裡面找,所以輸出會是is dog

作用域(Scope)

JavaScript的作用域有三個,全域性作用域、區域性作用域、塊級作用域

1、全域性作用域

這個就是說全域性可訪問,在瀏覽器下,就是直接掛載在window物件下面的屬性,都是全域性作用域的

// 全域性作用域

var name = ‘張三’ // 定義全域性變數name

console。log(name)

function showName() {

console。log(name) // 可以訪問全域性變數

}

showName() // 張三

2、區域性作用域

一般在函式內容透過變數宣告的,都是區域性作用域,一個函式定義,就重新定義了一個作用域,不過父函式的區域性變更是可以被子函式使用的。

全域性和區域性作用域,就相關於國家法律和公司法規,公司裡面,肯定還是要在國家法律的規定內執行的,但A公司的公司法規,就不能放到B公司執行了。

function callLi() {

var name = ‘李四’

console。log(name)

}

function callZh() {

console。log(name)

}

callLi() // 李四

callZh() // undefined

3、塊級作用域

這個其實是ES6引入let和const關鍵字後加的,使用let和const關鍵字宣告的變數,就會形成一個塊級作用域

if (true) {

let name = ‘張三’

const sex = ‘男’

}

console。log(name) // name is not defined

console。log(sex) // sex is not defined

上下文(Context)

很多人把作用域和上下文誤解為相同的概念,但事實並非如此。上下文是用來指定程式碼某些特定部分中 this 的值。作用域是指變數的可訪問性,上下文是指 this 在同一作用域內的值。

反正,你記得,那個上下文字就是this在同一作用域內的值。

當然,在ES6之前,由於函式呼叫後作用域的變化,會導致上下文指向的變化,所以一般會使用call(),apply(),bind()來改變上下文字,但處理過程比較不友好,所以ES6中,我們直接使用箭頭函式來處理。

如果你感覺上下文你總是記不住呼叫了哪個物件,那你堅持一個原理:

this是在執行時確認,且永遠指向最後呼叫它的那個物件

下圖是網路上找的一張上下文指導圖,大家可以參考下:

十萬個Web前端面試題之作用域與上下文

執行上下文(Execution Context)

JavaScript引擎在執行每個函式例項時,會建立一個執行上下文,執行期上下文有建立和程式碼執行的兩個階段。

1、建立階段

建立啟用物件

建立作用域鏈

設定上下文值this

2、執行階段

即程式碼執行

執行上下文棧

而多個執行上下文,又形成了執行上下文棧,這邊從網上找了一張圖,更生動

十萬個Web前端面試題之作用域與上下文

作用域鏈(Scope Chain)

在執行期上下文的建立階段,作用域鏈是在變數物件之後建立的。作用域鏈本身包含變數物件。作用域鏈用於解析變數。當被要求解析變數時,JavaScript 始終從程式碼巢狀的最內層開始,如果最內層沒有找到變數,就會跳轉到上一層父作用域中查詢,直到找到該變數或其他任何資源為止。作用域鏈可以簡單地定義為包含其自身執行上下文的變數物件的物件,以及其父級物件的所有其他執行期上下文,一個具有很多其他物件的物件。

這邊透過一個簡單的函式說明下作用域鏈

function add(num1, num2) {

var sum = num1 + num2

return sum

}

十萬個Web前端面試題之作用域與上下文