做用域是一套規則,用於肯定在何處以及如何查找變量。做用域查找會在找到第一個匹配的標識符時中止。
javascript
詞法做用域
定義在詞法階段的做用域。詞法做用域意味着做用域是由書寫代碼時函數聲明的位置決定的。
編譯的詞法分析階段基本可以知道所有標識符在哪裏以及是怎麼聲明的,從而可以預測在執行過程當中如何對它們進行查找。java
JavaScript中有兩個機制能夠「欺騙」詞法做用域:evel(...)和with。evel
能夠對一段包含一個或多個聲明的「代碼」字符串進行演算,並藉此來修改已經存在的詞法做用域(在運行時)。with
本質上是經過將一個對象的引用看成做用域來處理,將對象的屬性看成做用域中的標識符來處理,從而建立了一個新的詞法做用域(在運行時)。函數
這兩個機制的反作用是引擎沒法在編譯時對做用域查找進行優化,由於引擎只能謹慎地認爲這樣的優化是無效的。
使用這其中任何一個機制都將致使代碼運行變慢。不要使用它們。
優化
函數做用域
函數做用域是指屬於這個函數的所有變量均可以在整個函數的範圍內使用及複用(事實上在嵌套的做用域中也可使用)。code
塊級做用域
塊做用域指的是變量和函數不只能夠屬於所處的做用域,也能夠屬於某個代碼塊(一般指{...}內部)with、try/catch、let、const
對象
變量的賦值操做會經歷兩個動做,首先編譯器會在當前做用域中聲明一個變量
(若是以前沒有聲明過),而後再運行時引擎會在做用域中查找變量
,若是可以找到就會對它賦值
,不然引擎就會拋出一個異常。ip
引擎會在解釋javascript代碼以前首先對其進行編譯。編譯階段中的一部分工做就是找到全部的聲明,並用合適的做用域將他們關聯起來。每一個做用域都會進行提高操做
作用域
var a = 2; javascript實際上會將上述代碼當作兩個聲明: var a; 和 a = 2; 第一個定義聲明是在編譯階段進行,第二個賦值聲明會被留在原地等待執行階段。 eg1: a = 1; var a; console.log(a); //1 eg2: console.log(a); var a = 2; //undefined `只有聲明自己會被提高,而賦值或其餘運行邏輯會留在原地。`
function a(){ console.log(b) console.log(c) var b = 1 let c = 2 } a() 運行結果:undefined 和 ReferenceError foo() //不是ReferenceError而是TypeError bar() //ReferenceError var foo = function bar(){...} `函數聲明會被提高,函數表達式卻不會被提高` `即便是具名函數表達式,名稱標識符在賦值以前也沒法在所在做用域中使用`
foo() //3 function foo(){ console.log(1) } var foo = function(){ console..log(2) } function foo(){ console.log(3) } `函數聲明和變量聲明都會被提高,可是函數會首先被提高,而後纔是變量`
ReferenceError
引用錯誤,不存在的變量被引用時發生的錯誤
TypeError
類型錯誤,存在變量,值的類型非預期類型時發生的錯誤
匿名函數
function(){...} 沒有名稱標識符
具名函數
function something(){...} 有名稱標識符字符串
當即執行函數
函數被包含在一對()括號內部,所以成爲了一個表達式,經過在末尾加上另外一個()能夠當即執行這個函數,
例如:(function(){...})()
或(function(){...}())
編譯器
文章摘取來源:《你不知道的JavaScript上卷》