本書屬於基礎類書籍,會有比較多的基礎知識,所以這裡僅記錄平常不怎麼容易注意到的知識點,不會全記,供大家和自己翻閱;
上中下三本的讀書筆記:
第一部分 作用域和閉包
第二章 詞法作用域
詞法查詢
全域性變數會自動成為全域性物件(瀏覽器中是 window
) 的屬性,因此是不可以直接通過全域性物件的此法名稱,而是間接地通過全域性物件屬性的應用來對其進行訪問 window.a
,通過這種方法可以訪問那些被同名變數所遮蔽的全域性變數。但是如果非全域性的變數如果被遮蔽了,無論如何都無法被訪問到。
欺騙詞法
如果詞法作用域完全由寫程式碼期間函式所生命的位置來定義,那麼可以通過幾種方法來欺騙(修改)詞法作用域,比如 eval
、with
但是要注意:欺騙詞法作用域會導致效能下降。
因為JS引擎會在編譯階段進行效能優化,其中有些優化依賴於能夠根據程式碼的詞法進行靜態分析,並預先確定所有變數和函式的定義位置,才能在執行過程中快速找到識別符號。但是如果引擎在程式碼中找到 eval
、with
,就會完全不做任何優化。
第三章 函式作用域和塊作用域
函式作用域
包裝函式的宣告以 function
關鍵字開始,那麼就是函式宣告,而下面這個例子是以 (function
開始,那麼就是函式表示式:
const a = 1;
function foo() { // 函式宣告
const a = 4;
console.log(a);
}
(function foo() { // 函式表示式
const a = 3;
console.log(a);
}())
console.log(a);
複製程式碼
所以上面的 IIFE
將會被當做函式表示式而不是一個函式宣告來處理;
函式宣告和函式表示式之間最重要的區別是他們的名稱識別符號會繫結在何處。
函式宣告的名稱識別符號 foo
會被繫結在所在作用域中,可以直接通過 foo()
來呼叫;而函式表示式的 foo
被繫結在函式表示式只剩的函式中而不是所在作用域中;
同時,即使是具名的函式表示式,名稱識別符號在賦值之前也無法在所在作用域中使用。
try/catch
結構的 catch
分句中具有塊級作用域。
第四章 提升
編譯器
函式宣告會被提升,而函式表示式不會被提升。
函式優先
函式宣告和變數宣告都會被提升,但是函式會首先被提升,然後才是變數。
foo() // 1
var foo
function foo() {
console.log(1)
}
foo = function() {
console.log(2)
}
複製程式碼
函式宣告 foo
會首先被提升,然後列印出 1
,後面的 var
宣告會被認為是重複宣告而被忽略;但是注意如果後面出現同名函式宣告,則會覆蓋前面的:
foo() // 2
function foo() { console.log(1) }
function foo() { console.log(2) }
複製程式碼
第二部分 this和物件原型
第一章 關於this
this到底是什麼
this
實際上是在函式被呼叫時發生的繫結,它指向什麼完全取決於函式在哪裡被呼叫,並不是在編寫時繫結。當一個函式被呼叫時,會建立一個執行上下文,它包含函式在哪裡被呼叫(呼叫棧)、函式的呼叫方式、傳入的引數等資訊,this
就是這個記錄的一個屬性,會在函式執行的過程中用到。
判斷this
我們可以根據優先順序來判斷 this
:
-
new 繫結: 函式是否是在
new
中呼叫,如果是的話,this
繫結的是新建立的物件;var bar = new foo()
-
顯式繫結: 函式是否通過
call
、apply
或者硬繫結呼叫,如果是的話,this
繫結的是指定的物件;var bar = foo.call(obj)
-
隱式繫結: 函式是否在某個上下文物件中呼叫,如果是的話
this
繫結的是那個上下文物件;var bar = obj.foo()
-
預設繫結: 如果都不是的話,在嚴格模式下繫結到
undefined
,非嚴格模式繫結到全域性物件;var bar = foo()
例外
- 被忽略的情況: 比如把
null
、undefined
作為this
的繫結物件傳入call
、apply
、bind
,那麼這些值在呼叫時會被忽略,實際應用的是預設繫結; - 箭頭函式: 箭頭函式根據外層作用域來決定
this
,且箭頭函式的繫結無法被修改,new
也不可以;
PS:歡迎大家關注我的公眾號【前端下午茶】,一起加油吧~