動圖學 JavaScript 之:做用域鏈(Scope Chain)

一個例子

今天咱們來用介紹一下 JS 中的做用域鏈,先來看一段代碼:javascript

const name = "Lydia"
const age = 21
const city = "San Francisco"


function getPersonInfo() {
  const name = "Sarah"
  const age = 22

  return `${name} is ${age} and lives in ${city}`
}

console.log(getPersonInfo())

上面調用了函數 getPersonInfo,而後返回了一個包含 nameagecity 的字符串:java

Sarah is 22 and lives in San Franciscosegmentfault

可是 getPersonInfo 這個函數中並無 city 變量,那麼它是怎麼訪問到 city 的呢?瀏覽器

首先,不一樣的執行上下文會分配不一樣的內存空間。咱們有默認的全局上下文(瀏覽器中是 window,Node 中是 global),函數 getPersonInfo 在被調用時也會建立一個 本地上下文。每一個執行上下文都有一個 做用域(鏈)函數

對於函數 getPersonInfo,它的做用域鏈就像下圖這樣:oop

1-local.png

做用域鏈基本上是對 對象(即上圖中的 activation object 和 global object)的 「引用鏈」,這些對象中包含了對該執行上下文中 可引用的變量及其餘做用域 的引用。而且,做用域鏈是在執行上下文被建立的時候建立的,即這一切發生在 運行時spa

簡單來講,做用域(鏈)裏面存儲着執行上下文須要訪問的變量。翻譯

然而,本篇先不講 activation object 或者 執行上下文,咱們先把注意力放在 做用域(鏈) 上!

本地做用域

在下圖的例子中,執行上下文中的鍵值對即表明了做用域鏈對變量的引用。code

2-scope.png

全局執行上下文中有三個變量:對象

  • name = Lydia
  • age = 21
  • city = San Francisco

本地執行上下文中有兩個變量:

  • name = Sarah
  • age = 22

當咱們在函數 getPersonInfo 中試圖訪問變量時,JS 引擎會首先檢查本地做用域鏈。

3-local-scope.gif

本地做用域鏈能夠訪問到 nameage ,它們的值分別是 Sarah22。可是當須要訪問 city 時會發生什麼呢?

全局做用域

爲了訪問 city 變量,JS 引擎會 順着做用域鏈查找。能夠說 JS 引擎是個耿直 boy,你提的要求他不會輕易放棄,他會沿着做用域鏈一直找 city 變量,直到找到頂層 global object 位置。

4-global-scope.gif

在全局做用域,因爲咱們定義了 city 的值爲 San Francisco。因此函數拿到了這個值,而且打印出了 Sarah is 22 and lives in San Francisco

三維模型

咱們說順着做用域鏈查找,可能說向上和向下都有歧義,那麼就統一更正爲向 外層 做用域查找好了!看下面的 3D 圖:

5-fall1.png

或者更多層的話:

6-fall2.png

最初的例子

咱們再來看最開始的例子:

7-example.png

上圖和最開始的例子幾乎是同樣的,只不過把 city 定義在了函數 getPersonInfo 中。咱們尚未調用函數 getPersonInfo,因此本地做用域也沒有建立。那麼當咱們在全局做用域中訪問 nameagecity 時:

8-backwards.gif

程序拋出 ReferenceError!由於在全局做用域中找不到變量 city ,而且已經沒有再外層的做用域了,即無法順着做用域鏈找下去。

除了全局做用域和本地做用域,還有一個是 塊級做用域,當咱們在花括號 { } 中用 let 或者 const 關鍵字定義變量時,就產生了塊級做用域。

const age = 21

function checkAge() {
  if (age < 21) {
    const message = "You cannot drink!"
    return message
  } else {
    const message = "You can drink!"
    return message
  }
}

用虛線表示做用域以下:

9-example-scopes.png

咱們有一個全局做用域、一個函數做用域和兩個塊級做用域。也正是因爲塊級做用域的存在,因此咱們能定義 message 兩次。

總結

咱們來回顧一下:

  1. 你能夠將「做用域鏈」視爲對 可在當前上下文中訪問的值 的引用鏈。
  2. 做用域還可讓咱們重用變量名,只要不在同一個做用域中,變量名就能夠重複。

本篇就到這裏啦,本文是翻譯的系列文章:

  • 動圖學 JS 之:聲明提高(Hoisting)
  • 動圖學 JavaScript 之:做用域鏈(Scope Chain)【本篇】
  • 動圖學 JS 之:事件循環(Event Loop)【Pending】
  • 動圖學 JS 之:JavaScript 引擎 【Pending】

參考資料


本文首發於公衆號:碼力全開(codingonfire)

歡迎關注獲取最新內容哦~

codingonfire.jpg

相關文章
相關標籤/搜索