最近剛剛看完了《你不知道的 JavaScript》上捲,對 JavaScript 有了更進一步的瞭解。 《你不知道的 JavaScript》上捲由兩部分組成,第一部分是《作用域和閉包》,第二部分是《this 和對象原型》。下麵我會按照簡單介紹一下每一章的主要內容及閱讀感受。 第一部分《作用域和閉包》 ...
最近剛剛看完了《你不知道的 JavaScript》上捲,對 JavaScript 有了更進一步的瞭解。
《你不知道的 JavaScript》上捲由兩部分組成,第一部分是《作用域和閉包》,第二部分是《this 和對象原型》。下麵我會按照簡單介紹一下每一章的主要內容及閱讀感受。
第一部分《作用域和閉包》
第 1 章 作用域是什麼
- 編譯原理:簡單介紹分詞/詞法分析、解析/語法分析、代碼生成的概念;
- 理解作用域:介紹引擎、編譯器、作用域之間的關係;
- 作用域嵌套。
在這一章節中,作者通過引擎、編譯器、作用域之間的對話,將這三者之間的關係及作用生動形象地展現出來,並引出了 LHS 查詢和 RHS 查詢的概念。
第 2 章 詞法作用域
- 詞法作用域及其相關概念;
- 欺騙詞法的方式:
- 在代碼運行時修改詞法作用域,如
eval()
; - 在代碼運行時創建新的詞法作用域,如
with
。
- 在代碼運行時修改詞法作用域,如
這一章作者介紹了詞法作用域以及欺騙詞法的方式。說來慚愧,在看這章之前,我完全沒聽說過「詞法作用域」這個概念,一開始我還以為是個很高大上的東西,看完之後你會覺得其實也沒什麼,就是你平時都在寫的東西,只不過你沒有留意而已。
第 3 章 函數作用域和塊作用域
- 函數作用域:函數聲明和函數表達式的區別、具名函數和匿名函數;
- 塊作用域:
with
、try/catch
、let
、const
。
這一章作者介紹了 JavaScript 中的函數作用域及塊作用域,講了函數聲明和函數表達式的區別,其實很簡單,就是看 function
這個關鍵字是否是在聲明中的第一個詞,如果是,那就是函數聲明,否則就是函數表達式。另外,作者還簡單地介紹了下 ES6 中具有塊作用域作用的 let
和 const
關鍵字。
在這之前,我一直以為 ES6 之前是沒有塊作用域的,只有全局作用域和函數作用域,看完這章之後,我才知道其實在 ES3 的時候就有塊作用域了。比如,with
。再比如,try/catch
中的 catch
,一般我們是這樣寫的:
try {
// do something
} catch (err) {
console.log(err)
}
其中這個 err
只存在 catch
分句內部,從別處引用時會拋出錯誤。這不就是塊作用域嗎?
第 4 章 提升
這一章節作者簡單地介紹了一下變數聲明提升和函數聲明提升。沒什麼好說的,需要註意的是函數表達式是賦值操作,並不會提升。
第 5 章 作用域閉包
- 閉包;
- 作用域和閉包;
- 模塊機制。
閉包是 JavaScript 中的一大難點,在這章中作者用了 4 個小節來介紹閉包,還有 1 個小節來介紹模塊機制。不要看閉包有四個小節,其實也不過 8 頁而已,核心的文字加起來也就 2 頁,但就是這短短的 2 頁,就把閉包給講得非常清楚。
下麵是書中給出關於閉包的定義:
當函數可以記住並訪問所在的詞法作用域時,就產生了閉包,即使函數是在當前詞法作用域之外執行。
看了是不是還是不懂,沒關係,讓我們來提取關鍵字:
- 函數;
- 記住並訪問所在的詞法作用域;
- 當前詞法作用域之外執行。
再來看下書中的一段代碼,看完之後再結合書中的定義來理解,我相信你對閉包肯定會有更進一步的理解。
function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
var baz = foo();
baz(); // 2 —— 朋友,這就是閉包的效果。
下麵結合我們剛剛提取的關鍵字來理解。
- 函數。這裡的函數是
bar()
; - 記住並訪問。
bar()
當前所在的詞法作用域是foo()
的函數作用域。bar()
的詞法作用域能夠訪問foo()
的內部作用域。 - 當前詞法作用域之外執行。在上面的代碼中,我們將函數
bar()
當做一個值類型傳遞給外部,在這句代碼中var baz = foo();
,我們將foo()
的返回值(也就是bar()
)賦值給變數baz
並調用baz()
,實際上就是調用bar()
。上面第 2 點里我們說了,bar()
的作用域是foo()
的函數作用域,但是,在這裡,它卻是在自己定義的詞法作用域以外的地方執行。
怎麼樣,通過上面的分析,是不是對閉包有了進一步的理解了。
附錄 A 動態作用域
作者在這一章中簡單地分析了下動態作用域,並通過一小段代碼將它與詞法作用域做了對比。詞法作用域與動態作用域的主要區別在於:詞法作用域是在定義時確定的,而動態作用域是在運行時確定的。
附錄 B 塊作用域的替代方案
這一章簡單地介紹了塊作用域的替代方案 Traceur,以及因此可能會帶來的性能問題。
附錄 C this 詞法
這一章並沒有說明 this
機制 ,只是介紹了 ES6 中的箭頭函數引入的行為 —— this
詞法。關於 this
機制的詳細說明是在第二部分《this 和對象原型》中的第 1 章和 第 2 章。
附錄 D 致謝
這一章作者致謝了一大堆的人,光人名的排版就占了兩頁多,說真的,我都懷疑是不是在湊字數了(純調侃,沒別的意思)。
第二部分《this 和對象原型》
第 1 章 關於 this
- this 的指向;
- this 的作用域。
這一章中作者先是提出我們「為什麼要使用 this?」這個問題,然後再指出「this 到底是什麼?」,為第 2 章做鋪墊。
這一章我個人認為最核心的就是兩句話。第一句是「當一個函數被調用時,會創建一個活動記錄(有時也稱為執行上下文)。這個記錄會包含函數在哪裡被調用(調用棧)、函數的調用方式、傳入的參數等信息。this
就是這個記錄的一個屬性,會在函數執行的過程中用到」。也就是說,this
是活動記錄里的一個屬性,與函數執行的過程有關。
第二句話是「this 實際上是函數被調用時發生的綁定,它指向什麼完全取決於函數在哪裡被調用。」。第 2 章實際上就是在講這個綁定。
第 2 章 this 全面解析
- 調用位置;
- 綁定規則:
- 預設綁定;
- 隱式綁定;
- 顯式綁定:
call()
、apply()
、bind()
; - new 綁定;
- 箭頭函數的綁定;
- 一些例外的綁定。
- 綁定規則的優先順序。
作者在這一章中全面介紹了 this
的綁定規則。
要弄清楚 this
的綁定對象,需要明白以下兩點:
- 調用位置
- 綁定規則
什麼是調用位置?簡單來說,就是函數在代碼中被調用的位置。為了找到調用位置,我們需要分析調用棧,也就是為了到達當前執行位置所調用的所有函數,而調用位置就在當前正在執行的函數的前一個調用中。
而綁定規則就是說 this
綁定的對象是有規則的,並且這些規則是有優先順序的,總的來說有下麵四點:
- 由
new
調用的,綁定到新創建的對象; - 由
call
、apply
、bind
調用的,綁定到指定的對象; - 由上下文對象調用的,綁定到該上下文對象;
- 預設的,在嚴格模式下綁定到
undefined
,在非嚴格模式下綁定到全局對象。
當然了,ES6 中新增的箭頭函數並不在這四條規則裡面,而是繼承外層第一個非箭頭函數調用的 this
綁定。
在看這一章之前,我對 this
一知半解,網上找的答案也是五花八門,根本不知道哪個對哪個錯。在看完這一章之後,我算是對 this
的所綁定的對象有了較為清晰的認識,以後再遇到類似的問題時,直接套用上面的規則就可以了。
第 3 章 對象
- JavaScript 中的數據類型;
- 內置對象;
- 對象屬性與方法;
- 數組;
- 對象複製;
- 屬性描述符;
- [[Get]] 操作與 [[Put]] 操作;
- Getter 和 Setter;
- 遍歷及 ES6 中的 Symbol.iterator。
這一章講到了很多平時我並沒有註意到的東西,比如,一般來說,我們使用數組的時候都是下標/值對,但是,給數組添加屬性居然也是可以,雖然這並不會改變數組的長度。當我看到這一部分的內容時心裡在想:我去,這是什麼騷操作?這樣居然也可以?後面想了想,數組其實也是對象,是一個特殊的對象,從這一方面來說也是行得通的;再比如,屬性訪問與賦值時發生的 [[Get]] 操作與 [[Put]] 操作,能夠更好地瞭解其工作原理;還有,我們可以利用 ES6 中的 Iterator 介面實現自己的迭代邏輯。
第 4 章 混合對象 “類”
- 類理論;
- 類的機制;
- 類的繼承;
- 混入。
這一章講到了 “類” 這一設計模式,以及 JavaScript 中各種實現這一模式的方法。
這一章的核心就是:類的本質是複製,多態和繼承也是。這一點很重要,JavaScript 中也有類,但是兩者的本質是不同的,這一點在《第 5 章 原型》和《第 6 章 行為委托》裡面有詳細的說明。
這一章一開始看的時候我是很模糊的,因為作為一名 JavaScript 開發者,說實話我對於 “類” 這個東西的理解不是很明白,所以我跳過了這一章,等到看完了後面三章之後再回過來看,瞬間感覺清晰很多了。
第 5 章 原型
- [[Prototype]] 屬性;
- 屬性設置和屏蔽;
- JavaScript 中的 “類”;
- (原型)繼承;
- 對象關聯。
這一章中,第一小節的 [[Prototype]] 屬性可以和第 3 章中的 [[Put]] 操作結合一起看,這樣能夠完整的瞭解屬性賦值的工作原理;屬性設置和屏蔽這一部分可以和第 4 章結合著閱讀,以便更好地瞭解 JavaScript 中的類與其它語言中的類的區別。
第 6 章 行為委托
這一章作者主要從類理論與委托理論(其實也就是對象關聯)兩種不同的設計模式來介紹他們之間在代碼上實現的不同,可以看做是第 4 章和第 5 章的對象關聯的實踐部分。
附錄 A ES6 中的 Class
這一章作者分析了 ES6 中新增的 Class 語法的優點與缺點。
全書感悟
以上就是本書中的一些主要內容介紹,我寫得比較簡單,其實書中還有很多比較細小的東西,有興趣的同學可以去買來看看。總的來說,這本書還是挺不錯的,能讓你學到一些平時沒有註意到的東西,作者偏向於用口語化的文字來介紹知識點,不會顯得枯燥。
最後,拋塊磚,希望能引塊玉。下麵是我在閱讀本書過程中的做的思維導圖。導圖的內容比較多,不是很簡潔,因為我希望儘量把書中作者提到的概念提取出來,所以可能會顯得比較啰嗦。