您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關javascript中編譯原理,作用域,作用域鏈,變量提升,暫時性死區是什么,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
我們先來看一行代碼
var name='jack';復制代碼
在我們眼中,這是一行代碼,一個語句就能搞定的事情呀,但是,在JS的眼里,這句話的代碼應該是下面這種方式呈現的
var name; //編譯階段處理name='jack'; //執行階段處理復制代碼
是不是發現這和你原本認識的JS不太一樣,那是因為JS編譯主要分為兩個階段,編譯階段和執行階段,讓我們首先來看下這兩個階段分別做了那些事情:
這個階段的主角就是所謂的編譯器,這個編譯器會找遍當前作用域,看看是不是已經存在一個叫 name
的變量。如果已經存在,那么就什么都不做,直接忽略 var name
這個聲明,繼續編譯下去;如果沒有,則在當前作用域里新增一個叫 name
的變量。然后,編譯器會為引擎生成運行時所需要的代碼,程序就進入了執行階段
這個階段的主角就是大家所熟悉的JS引擎啦,JS引擎在運行的時候,也會先找遍當前作用域,看看是否有一個叫 name
的變量,如果有的話,那么剛剛好,直接賦值,如果沒有的話,那就是當前作用域沒有,那怎么辦,那就考慮探出頭去,去外面( 父級作用域 )看看有沒有,沒有的話,再去外面查找,一層又一層( 當然如果是還有父層的話 ),如果最終還是找不到的話,那么JS引擎也表示無能為力,那就拋個異常給別人看看吧,表示自己已經努力過了。
上面提到的去外面查找,一層又一層,從當前作用域再到父級作用域,再到父級的父級作用域,以此類推,就是所謂的作用域鏈了,就跟鏈條一樣,一節有一節往上,是不是形容地可以說是很貼切了。總結而言就是,作用域套作用域,也就有了所謂的作用域鏈
大家都知道,變量最基本的能力就是能夠存儲變量當中的值、并且允許我們對這個變量的值進行訪問和修改,而對于變量存儲,訪問的一套規則,就是所謂的作用域
在任何函數外或者代碼塊之外的頂層作用域就是全局作用域,而里面的變量就是全局變量
var name='jack'; //全局作用域function showName(){ //函數作用域 console.log(name);}{ name='test'; //塊級作用域}showName(); //test復制代碼
可以看到全局變量,無論是在全局作用域,函數作用,還是塊級作用域中都可以正常訪問
在函數內的作用域就是函數作用域
function showName(){ var name='jack'; //函數作用域}showName(); //方法調用{ console.log(name); //塊級作用域,Uncaught ReferenceError: name is not defined}console.log(name); //全局作用域,Uncaught ReferenceError: name is not defined復制代碼
可以看到函數內部變量,在全局作用域以及塊級作用域中,都無法訪問,只有在函數內部,才能訪問的到,所以函數內的變量也被稱為局部變量
在 ES6
中新出的兩個新關鍵字 let
和 const
中,自帶塊級作用域,塊級作用域相當于是只在這塊代碼塊中生效,如果它被大括號 {}
所包圍,那么大括號中就是一段代碼塊,代碼塊中使用 let
和 const
聲明的變量也被稱為局部變量
{ let name='jack'; } console.log(name); //Uncaught ReferenceError: name is not defined function showName{ console.log(name); } showName(); //Uncaught ReferenceError: name is not defined復制代碼
可以看到塊級作用域中的變量,出了那個代碼塊,就找不到了
其實上面的三種情況,結合JS編譯原理和作用域鏈向外不向內查找,思考一下,也不難理解
回到作用域鏈,其實在上面已經解釋的差不多了,作用域和作用域的嵌套,就產生了作用域鏈,另外要記住的一個特性就是作用域鏈的查找,向外不向內,想想探出頭去,而不是看著鍋里,就可以了
先來看一段代碼
name='jack';console.log(name); //jackvar name;復制代碼
你會發現,這段代碼不會發生報錯,并且能正常地運行,結合上面所說的JS編譯原理,你就能想到,在JS的眼中,它的代碼實際上是這樣子的,這就是所謂的變量提升,說白了那就是代碼的聲明提到代碼的最前面
var name;name='jack';console.log(name); //jack復制代碼
其實這個變量提升應該是照道理接著編譯原理寫下來的,為什么放到了最后呢,因為如果你忘了,正好往上翻一下,重新回溫一遍JS編譯原理
緊接著上面,讓我們來看下不吃變量提升這一套的 let
和 const
,先來看一段代碼
name='jack';console.log(name) //Uncaught ReferenceError: Cannot access 'name' before initializationlet name;復制代碼
黑人問號 ??? ,說好的變量提升呢,記住 let
和 const
的一個特點,禁用變量提升,這也是 ES6
故意為之的,將生命前不可用做到了強約束,總結而言,** var
存在變量提升, let
和 const
不存在變量提升**
既然已經提到了 const
,順帶提一下它聲明了以后必須賦值的操作
const name; //Uncaught SyntaxError: Missing initializer in const declaration復制代碼
緊接著上面,讓我們來看下什么叫做暫時性死區,先來看一段代碼
var name='jack';{ name='bob'; let name; //Uncaught ReferenceError: Cannot access 'name' before initialization}復制代碼
記住 ES6
中的一個特性,如果區塊中存在 let
和 const
命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。因為JS清楚地感知到了 name
是用 let
聲明在當前這個代碼塊內的,所以會給這個變量 name
加上了暫時性死區的限制,它就不往外探出頭了。
那么,如果我們把上面的let name;
去掉,程序也將正常運行, name
的值也被成功修改為了bob,就是正常地按照作用域鏈的規則,向外探出頭去了。
關于javascript中編譯原理,作用域,作用域鏈,變量提升,暫時性死區是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。