JavaScript中奇怪的一點是你能夠在變量和函數聲明以前使用它們。就好像是變量聲明和函數聲明被提高了代碼的頂部同樣。javascript
sayHi() // Hi there!
function sayHi() {
console.log('Hi there!')
}
name = 'John Doe'
console.log(name) // John Doe
var name
複製代碼
然而JavaScript並不會移動你的代碼,因此JavaScript中「變量提高」並非真正意義上的「提高」。java
JavaScript是單線程語言,因此執行確定是按順序執行。可是並非逐行的分析和執行,而是一段一段地分析執行,會先進行編譯階段而後纔是執行階段。數據結構
在編譯階段階段,代碼真正執行前的幾毫秒,會檢測到全部的變量和函數聲明,全部這些函數和變量聲明都被添加到名爲Lexical Environment的JavaScript數據結構內的內存中。因此這些變量和函數能在它們真正被聲明以前使用。函數
sayHi() // Hi there!
function sayHi() {
console.log('Hi there!')
}
複製代碼
由於函數聲明在編譯階段會被添加到詞法環境(Lexical Environment)中,當JavaScript引擎遇到sayHi()
函數時,它會從詞法環境中找到這個函數並執行它。ui
lexicalEnvironment = {
sayHi: < func > } 複製代碼
console.log(name) // 'undefined'
var name = 'John Doe'
console.log(name) // John Doe
複製代碼
上面的代碼實際上分爲兩個部分:this
var name
表示聲明變量name
= 'John Doe'
表示的是爲變量name
賦值爲'John Doe'。var name // 聲明變量
name = 'John Doe' // 賦值操做
複製代碼
只有聲明操做var name
會被提高,而賦值這個操做並不會被提高,可是爲何變量name
的值會是undefined
呢?spa
緣由是當JavaScript在編譯階段會找到var
關鍵字聲明的變量會添加到詞法環境中,並初始化一個值undefined
,在以後執行代碼到賦值語句時,會把值賦值到這個變量。線程
// 編譯階段
lexicalEnvironment = {
name: undefined
}
// 執行階段
lexicalEnvironment = {
name: 'John Doe'
}
複製代碼
因此函數表達式也不會被「提高」。helloWorld
是一個默認值是undefined
的變量,而不是一個function
。code
helloWorld(); // TypeError: helloWorld is not a function
var helloWorld = function(){
console.log('Hello World!');
}
複製代碼
console.log(a) // ReferenceError: a is not defined
let a = 3
複製代碼
爲何會報一個ReferenceError
錯誤,難道let
和const
聲明的變量沒有被「提高」嗎?xml
事實上全部的聲明(function, var, let, const, class)都會被「提高」。可是隻有使用var
關鍵字聲明的變量纔會被初始化undefined
值,而let
和const
聲明的變量則不會被初始化值。
只有在執行階段JavaScript引擎在遇到他們的詞法綁定(賦值)時,他們纔會被初始化。這意味着在JavaScript引擎在聲明變量以前,沒法訪問該變量。這就是咱們所說的Temporal Dead Zone,即變量建立和初始化之間的時間跨度,它們沒法訪問。
若是JavaScript引擎在let
和const
變量被聲明的地方還找不到值的話,就會被賦值爲undefined
或者返回一個錯誤(const
的狀況下)。
舉例:
let a
console.log(a) // undefined
a = 5
複製代碼
在編譯階段,JavaScript引擎遇到變量a
並將它存到詞法環境中,但由於使用let
關鍵字聲明的,JavaScript引擎並不會爲它初始化值,因此在編譯階段,此刻的詞法環境像這樣:
lexicalEnvironment = {
a: <uninitialized> } 複製代碼
若是咱們要在變量聲明以前使用變量,JavaScript引擎會從詞法環境中獲取變量的值,可是變量此時仍是uninitialized
狀態,因此會返回一個錯誤ReferenceError
。
在執行階段,當JavaScript引擎執行到變量被聲明的時候,若是聲明瞭變量並賦值,會更新詞法環境中的值,若是隻是聲明瞭變量沒有被賦值,那麼JavaScript引擎會給變量賦值爲undefined
。
tips: 咱們能夠在let
和const
聲明以前使用他們,只要代碼不是在變量聲明以前執行:
function foo() {
console.log(name)
}
let name = 'John Doe'
foo() // 'John Doe'
複製代碼
同let
和const
同樣,class
在JavaScript中也是會被「提高」的,在被真正賦值以前都不會被初始化值, 一樣受Temporal Dead Zone的影響。
let peter = new Person('Peter', 25) // ReferenceError: Person is not defined
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
let John = new Person('John', 25);
console.log(peter) // Person { name: 'John', age: 25 }
複製代碼