讀《你不知道的JS》的筆記,有問題請指出。
html
每一個編程語言的一個最基本的功能,就是能夠聲明變量,在變量裏儲存值,更改值,訪問值。
隨之一系列問題產生,這些變量存儲在哪裏,未來須要使用他們的時候如何獲取他們?
這表明咱們須要有一套設計良好的規則知道如何存儲這些變量,並如何獲取到他們,而這套規則,被稱爲做用域(Scope)。編程
不只是在執行時會用到做用域,編譯時也會用到做用域編程語言
咱們用 JS 寫的代碼稱爲源代碼,是一種人類能看懂的語言,因爲計算機只能讀懂 0 和 1(二進制/機器語言),因此若是咱們要在計算機上執行咱們的代碼,在執行代碼以前,有個編譯過程,目的是將源碼編譯成機器能夠理解的機器碼。函數
通常來講,編譯分爲 3 個步驟設計
var a = 2
這行代碼,當詞法單元生成器(tokenizer)在判斷 var
是單獨的一個詞法單元仍是屬於其餘詞法單元的一部分時,若是調用的是有狀態(stateful)的解析規則,那麼字符串被轉換成詞法單元的這個過程就被稱爲詞法分析,不然,就是分詞。var a = 2
這一系列任務。編譯完以後,就是執行代碼了,對於 JS 來講,即便是 var a = 2
這行很簡單的代碼,在通過編譯和執行這兩個步驟時,也會涉及到做用域。code
JS 的編譯過程和其餘語言的編譯過程有點不一樣,在編譯的第三步,也就是將抽象語法樹(AST)轉換成機器指令時,當編譯器碰到聲明操做時,如 var a = 2
,編譯器會詢問做用域,在當前的做用域內,是否聲明過 a,若是沒有,就會讓做用域在當前做用域內聲明一個 a,不然,忽略該聲明。htm
從上面能夠看出,在 JS 引擎真正執行代碼以前,編譯過程當中,編譯器就會將變量先在做用域內聲明好。token
待解決:上面用的是 var,那麼我用 let 聲明變量的話,他也會幫我提早在做用域內聲明好嗎?作用域
代碼編譯完成後,就是執行步驟了,執行是由 JS 引擎執行,在執行 var a = 2
這行代碼時,也會涉及到做用域:字符串
便於理解,接下來咱們把它們擬人化,每一個人工做上都有本身的工做職責,他們也是同樣,對於做用域來講,他的職責就是管理他這塊區域的變量,這個區域裏,存在哪些變量,變量分別存儲的值是多少他都知道,也是他應該知道的,因此在以前的編譯過程當中,編譯器在聲明 a 以前,首先跑去問了做用域確認這塊區域裏尚未變量 a 後,才讓做用域在這塊區域內聲明瞭變量 a。
而編譯完成後,也就是代碼執行過程,JS 引擎這時看到 var a = 2
後,也會先去問做用域,在當前做用域下,是否已經存在變量 a 了呢?
做用域檢查了下當前的他這塊區域內的變量,回答說,「嗯嗯,已經有了,是剛剛編譯的時候編譯器聲明的」。
JS 引擎:「好嘞,既然有了,那我就不用重複聲明瞭,我就賦個值就好了。」
LHS && RHS
JS 引擎在向做用域詢問變量的時候,查詢的方式還能夠細分爲 LHS 和 RHS,也就是讓做用域查詢這個變量是否存在,仍是讓做用域查詢這個變量的值。
var a = 2 console.log(a)
代碼如上,仍是以對話的形式
JS 引擎:做用域大哥,幫我看看你那裏有沒有變量 a 啊,我得給他賦個值。(查看變量容器自己是否存在,屬於 LHS 查詢方式) 做用域:找到了!這傢伙在我這 JS 引擎: 謝謝了,再幫我看看 console 變量的值呢,我找找他裏面有沒有 log 這個方法(查詢 console 變量的值,屬於 RHS 查詢方式) 做用域:有的,console 的值給你了,你看看吧 JS 引擎:好嘞,謝謝,我看一下。。有 log 這個方法!做用域,再幫我看下 a 變量的值呢,雖然我剛剛給他賦值了一個 2,可是我仍是得確認一下(查詢變量 a 的值,屬於 RHS 查詢方式) 做用域:嗯嗯,我看了下,他的值仍是2 JS 引擎:好的,謝謝!
有的時候,做用域是嵌套的
// 最外層爲全局做用域 var a = 2 function test() { // 對於 JS 來講,一個函數會生成一個做用域(先不提 let 生成的塊級做用域) console.log(a) } test()
對於 test 函數來講,他本身內部就有一個做用域 A,最外層有一個全局做用域。
JS 引擎在調用 test 函數的時候,因爲須要打印變量 a,所以向 test 函數內部的做用域 A 求助,問他有沒有看到變量 a,做用域 A 說沒有看到,因而 JS 引擎向做用域 A 外層的做用域(全局做用域)求助,最終在全局做用域裏找到了變量 a。
有時候並不必定當前做用域的外層做用域就是全局做用域,可能還嵌套有其餘做用域,可是查詢方式都是同樣的,當前做用域找不到,就沿着嵌套的做用域往外找,直到找到全局做用域。
若是在全局做用域內也找不到這個變量呢?
那就可能會報錯了,可是具體的報錯信息仍是有區別的:
查詢變量自己(LHS 查詢方式)
console.log.test.sss
,從一個 undefined
的數據類型上獲取 sss
屬性,就會拋出 typeError 錯誤