JavaScript專題之一:變數提升與預編譯
目錄
- #前言
- 一、有趣的現象
- 二、Js的預解析
- 三、提升之間的優先權
- 四、ES6
寫在最後
)
#前言- 本篇文章是《JavaScript專案進階系列》的第一篇文章,全系列大概會包含例如:
- 防抖節流
- #深淺拷貝
- 數組去重
- 排序
等等經典的專案知識點。取名為專案進階
是因為它們在許多場合的「出鏡率高」很高,為了避免化身
,《JavaScript專案進階系列》誕生了! ! !
一、有趣的現象
#依照大家的常識,JavaScript程式碼在執行是一定是自上而下的,你需要輸出一個字串,當然需要事先宣告一個
。如果深奧的道理我都能懂,於是我閱讀了下面的程式碼。
1.1 我以為的開局
var str = '123';console.log(str); // 123
我們調換程式碼的位置在再看:console.log(str); // undefinedvar str = '123';
#當我看完了前兩段程式碼並且進行了「深度思考」後,我好像找到規律了,那就是:在當前程式碼區塊後函數中,在變數宣告和初始化
使用變量,會拿不到正確的值。
1.2 其實是這樣的
帶著上面的「結論」我來到了這裡var val = '余光';(function(){
console.log(val); // 余光})();
耶穌也阻擋不了我拿到val的值,我說的! ! !
當我看到下面一段程式碼時,我已經產生了動搖,此事必要蹊蹺。
var val = '余光';(function(){ console.log(val); // undefined var val = '测试';})();
Ps:如果大家立即執行函數有疑問,不妨看看《JavaScript之深入理解立即調用函數表達式(IIFE)》吧~
這…我慫了,是什麼原因導致這樣的現象發生的呢? Js又是如果處理的呢?
二、Js的預解析
在目前的作用域內,無論在哪裡變數聲明,在幕後,都會進行一次看不見的移動。 注意:僅宣告被「移動」
。即聲明和賦值在某些時候被動分開了。而這次看不見的移動其實就是
。
來看一段《你知不知道的Js》中經典的例子:name = '余光'; // 未添加关键字(未声明),name为全局变量,,即window.name = '余光'var name; // 再次声明name,此时name未进行初始化,它的值是undefined吗?console.log(name); // ?
就發生在Js預解析(編譯)之中。
2.1 核心:預解析
為了搞清楚這個核心問題,我們需要回顧一下,引擎會在解釋JavaScript程式碼之前先將其編譯。編譯階段中的一部分工作就是找到所有的聲明,並用適當的作用域將它們關聯起來。有興趣的小夥伴可以閱讀《JavaScript中的變數物件》和《從作用域到作用域鏈》這兩篇文章哦~因此,發生這樣的事情,包括
變數
和函數
在內的所有宣告都會在任何程式碼被執行前先被處理。當你看到
- ;時,可能會認為這是一個宣告。但JavaScript實際上會將其看成兩個聲明:var a;和a = 2;。
- 第一個定義宣告是在編譯階段進行的。
即程式碼是這樣寫的:
// 我们看到的代码:var name = '余光';
但Js會將它解析成:
// 声明(Declaration)var name; // 声明但未初始化,所以分配 undefined// 初始化(Initialization)name = '余光'; // 初始化(赋值)
所以本小結的一段程式碼應該這樣分析:
var name; // 声明name提到作用域顶部,并被分配了一个undefinedname = '余光'; // 进行初始化操作console.log(name); // '余光'
2.2 注意:只有宣告被提升了
只有宣告會被提升,而賦值和其他程式碼邏輯會在執行到程式碼的位置時才會生效。所以會有下面的問題:
foo();function foo(){ console.log(name); // undefined var name = '余光';}
函數被提升了,自然可以正常執行,但變數只是宣告被提升了。
2.3 每個作用域都會進行提升操作
還是上面的程式碼:
foo();function foo(){ console.log(name); // undefined var name = '余光';}
實際上它在編譯時是這樣的:
function foo(){ var name; // 声明 console.log(name); // undefined name = '余光'; // 初始化}foo(); // 函数执行
三、提升之間的優先權
既然我們知道了
變數
和
3.1 函数会被首先提升,然后才是变量
我们分析下面的代码:
foo();var foo; // 1function foo(){ console.log('余光');}foo = function(){ console.log('小李');}
本着函数优先提升的原则,他会被解析成这样:
function foo(){ console.log('余光');}foo(); // 余光foo = function(){ console.log('小李');}
注意,var foo
因为是一个重复声明,且优先级低于函数声明
所以它被忽略掉了。
3.2 函数字面量不会进行函数提升
最直观的例子,就是在函数字面量前调用该函数:
foo();var foo = function(){ console.log(1);}// TypeError: foo is not a function
这段程序中:
- 变量标识符
foo
被提升并分配给所在作用域(在这里是全局作用域),因此在执行foo()时不会导致ReferenceError(),而是会提示你foo is not a function
。 - 然后就是执行foo,foo此时并没有赋值(注意变量被提升了)。由于对undefined值进行函数调用而导致非法操作,因此抛出TypeError异常。
四、ES6和小结
ES6新增了两个命令let
和const
,用来声明变量,有关它们完整的概念我会在《ES6基础系列》中总结,提起它们,是因为变量提升在它们身上不会存在。
4.1 变量提升是可以规避的
let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
// var 的情况console.log(foo); // 输出undefinedvar foo = 2;// let 的情况console.log(bar); // 报错ReferenceErrorlet bar = 2;
上面代码中,变量foo用var命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。
在变量提升上,const和let一样,只在声明所在的块级作用域内有效,也不会变量提升
4.2 小结
- 变量提升:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部,但变量的初始化不会提升;
- 函数提升:函数声明可以被看作是函数的整体被提升到了代码的顶部,但函数字面量表达式并不会引发函数提升;
- 函数提升优先与变量提升;
- let和const可以有效的规避变量提升
最后提炼一下:JavaScript引擎并不总是按照代码的顺序来进行解析。在编译阶段,无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理,这个过程被称为提升。声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升。
相关免费学习推荐:javascript(视频)
以上是JavaScript專題之一:變數提升與預編譯的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript中的HTTP狀態碼取得方法簡介:在進行前端開發中,我們常常需要處理與後端介面的交互,而HTTP狀態碼就是其中非常重要的一部分。了解並取得HTTP狀態碼有助於我們更好地處理介面傳回的資料。本文將介紹使用JavaScript取得HTTP狀態碼的方法,並提供具體程式碼範例。一、什麼是HTTP狀態碼HTTP狀態碼是指當瀏覽器向伺服器發起請求時,服務
