淺析JavaScript中的變量提升
前言:
JavaScript中奇怪的一點是你可以在變量和函數(shù)聲明之前使用它們。就好像是變量聲明和函數(shù)聲明被提升了代碼的頂部一樣。
sayHi() // Hi there! function sayHi() { console.log('Hi there!') } name = 'John Doe' console.log(name) // John Doe var name
然而JavaScript并不會移動你的代碼,所以JavaScript中“變量提升”并不是真正意義上的“提升”。
JavaScript是單線程語言,所以執(zhí)行肯定是按順序執(zhí)行。但是并不是逐行的分析和執(zhí)行,而是一段一段地分析執(zhí)行,會先進行編譯階段然后才是執(zhí)行階段。
在編譯階段階段,代碼真正執(zhí)行前的幾毫秒,會檢測到所有的變量和函數(shù)聲明,所有這些函數(shù)和變量聲明都被添加到名為Lexical Environmen
t的JavaScript數(shù)據(jù)結(jié)構(gòu)內(nèi)的內(nèi)存中。所以這些變量和函數(shù)能在它們真正被聲明之前使用。
函數(shù)提升
sayHi() // Hi there! function sayHi() { console.log('Hi there!') }
因為函數(shù)聲明在編譯階段會被添加到詞法環(huán)境(Lexical Environment)中,當(dāng)JavaScript引擎遇到sayHi()
函數(shù)時,它會從詞法環(huán)境中找到這個函數(shù)并執(zhí)行它。
lexicalEnvironment = { sayHi: < func > }
var變量提升
console.log(name) // 'undefined' var name = 'John Doe' console.log(name) // John Doe
上面的代碼實際上分為兩個部分:
var name
表示聲明變量name
= 'John Doe'
表示的是為變量name
賦值為'John Doe'。
var name // 聲明變量 name = 'John Doe' // 賦值操作
只有聲明操作var name
會被提升,而賦值這個操作并不會被提升,但是為什么變量name
的值會是undefined
呢?
原因是當(dāng)JavaScript在編譯階段會找到var
關(guān)鍵字聲明的變量會添加到詞法環(huán)境中,并初始化一個值undefined
,在之后執(zhí)行代碼到賦值語句時,會把值賦值到這個變量。
// 編譯階段 lexicalEnvironment = { name: undefined } // 執(zhí)行階段 lexicalEnvironment = { name: 'John Doe' }
所以函數(shù)表達式也不會被“提升”。helloWorld
是一個默認(rèn)值是undefined
的變量,而不是一個function
。
helloWorld(); // TypeError: helloWorld is not a function var helloWorld = function(){ console.log('Hello World!'); }
let & const提升
console.log(a) // ReferenceError: a is not defined let a = 3
為什么會報一個ReferenceError
錯誤,難道let
和const
聲明的變量沒有被“提升”嗎?
事實上所有的聲明(function, var, let, const, class)都會被“提升”。但是只有使用var
關(guān)鍵字聲明的變量才會被初始化undefined
值,而let
和const
聲明的變量則不會被初始化值。
只有在執(zhí)行階段JavaScript引擎在遇到他們的詞法綁定(賦值)時,他們才會被初始化。這意味著在JavaScript引擎在聲明變量之前,無法訪問該變量。這就是我們所說的Temporal Dead Zone,即變量創(chuàng)建和初始化之間的時間跨度,它們無法訪問。
如果JavaScript引擎在let
和const
變量被聲明的地方還找不到值的話,就會被賦值為undefined
或者返回一個錯誤(const
的情況下)。
舉例:
let a console.log(a) // undefined a = 5
在編譯階段,JavaScript引擎遇到變量a
并將它存到詞法環(huán)境中,但因為使用let
關(guān)鍵字聲明的,JavaScript引擎并不會為它初始化值,所以在編譯階段,此刻的詞法環(huán)境像這樣:
lexicalEnvironment = { a: <uninitialized> }
如果我們要在變量聲明之前使用變量,JavaScript引擎會從詞法環(huán)境中獲取變量的值,但是變量此時還是uninitialized
狀態(tài),所以會返回一個錯誤ReferenceError
。
在執(zhí)行階段,當(dāng)JavaScript引擎執(zhí)行到變量被聲明的時候,如果聲明了變量并賦值,會更新詞法環(huán)境中的值,如果只是聲明了變量沒有被賦值,那么JavaScript引擎會給變量賦值為undefined
。
tips: 我們可以在let
和const
聲明之前使用他們,只要代碼不是在變量聲明之前執(zhí)行:
function foo() { console.log(name) } let name = 'John Doe' foo() // 'John Doe'
Class提升
同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(John) // Person { name: 'John', age: 25 }
到此這篇關(guān)于淺析JavaScript中的變量提升的文章就介紹到這了,更多相關(guān)JS變量提升內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript異步調(diào)用定時方法并停止該方法實現(xiàn)代碼
JavaScript異步調(diào)用定時方法并停止該方法實現(xiàn)代碼 ,需要的朋友可以參考下2012-03-03js中document.referrer實現(xiàn)移動端返回上一頁
本文主要介紹了document.referrer實現(xiàn)移動端返回上一頁的方法,具有很好的參考價值,下面跟著小編一起來看下吧2017-02-02