首頁  >  文章  >  web前端  >  JavaScript專題之一:變數提升與預編譯

JavaScript專題之一:變數提升與預編譯

coldplay.xixi
coldplay.xixi轉載
2021-03-02 09:42:421768瀏覽

JavaScript專題之一:變數提升與預編譯

目錄

  • #前言
  • 一、有趣的現象
  • 二、Js的預解析
  • 三、提升之間的優先權
  • 四、ES6
  • 寫在最後

##(相關免費學習推薦:

javascript影片教學

#前言

    本篇文章是《JavaScript專案進階系列》的第一篇文章,全系列大概會包含例如:
  • 防抖節流
  • #深淺拷貝
  • 數組去重
  • 排序

等等經典的專案知識點。取名為專案進階是因為它們在許多場合的「出鏡率高」很高,為了避免化身

google內容搜尋師

,《JavaScript專案進階系列》誕生了! ! ! JavaScript專題之一:變數提升與預編譯

一、有趣的現象

#依照大家的常識,JavaScript程式碼在執行是一定是自上而下的,你需要輸出一個字串,當然需要事先宣告一個

保存string類型的變數
。如果深奧的道理我都能懂,於是我閱讀了下面的程式碼。

1.1 我以為的開局

var str = '123';console.log(str); // 123

我們調換程式碼的位置在再看:

console.log(str); // undefinedvar str = '123';

我好像找到規律了!!!

#當我看完了前兩段程式碼並且進行了「深度思考」後,我好像找到規律了,那就是:在當前程式碼區塊後函數中,在變數宣告和初始化

之前

使用變量,會拿不到正確的值。 JavaScript專題之一:變數提升與預編譯

1.2 其實是這樣的

帶著上面的「結論」我來到了這裡

var val = '余光';(function(){
    console.log(val); // 余光})();
果然如此! ,在

變數宣告和初始化之後

耶穌也阻擋不了我拿到val的值,我說的! ! !

當我看到下面一段程式碼時,我已經產生了動搖,此事必要蹊蹺。

var val = '余光';(function(){
    console.log(val); // undefined
    var val = '测试';})();

Ps:如果大家立即執行函數有疑問,不妨看看《JavaScript之深入理解立即調用函數表達式(IIFE)》吧~

這…我慫了,是什麼原因導致這樣的現象發生的呢? Js又是如果處理的呢? JavaScript專題之一:變數提升與預編譯

二、Js的預解析

在目前的作用域內,無論在哪裡變數聲明,在幕後,都會進行一次看不見的移動。 注意:僅宣告被「移動」。即聲明和賦值在某些時候被動分開了。而這次看不見的移動其實就是

Js在編譯階段的解析

來看一段《你知不知道的Js》中經典的例子:

name = '余光'; // 未添加关键字(未声明),name为全局变量,,即window.name = '余光'var name; // 再次声明name,此时name未进行初始化,它的值是undefined吗?console.log(name); // ?
結果是成功打印“余光”,這樣

看不見的移動
就發生在Js預解析(編譯)之中。

2.1 核心:預解析

為了搞清楚這個核心問題,我們需要回顧一下,引擎會在解釋JavaScript程式碼之前先將其編譯。編譯階段中的一部分工作就是找到所有的聲明,並用適當的作用域將它們關聯起來。有興趣的小夥伴可以閱讀《JavaScript中的變數物件》和《從作用域到作用域鏈》這兩篇文章哦~因此,發生這樣的事情,包括變數函數在內的所有宣告都會在任何程式碼被執行前先被處理。當你看到

var a = 2
    ;時,可能會認為這是一個宣告。但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 = '余光';}

實際上它在編譯時是這樣的:JavaScript專題之一:變數提升與預編譯

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

这段程序中:

  1. 变量标识符foo被提升并分配给所在作用域(在这里是全局作用域),因此在执行foo()时不会导致ReferenceError(),而是会提示你 foo is not a function
  2. 然后就是执行foo,foo此时并没有赋值(注意变量被提升了)。由于对undefined值进行函数调用而导致非法操作,因此抛出TypeError异常。

四、ES6和小结

ES6新增了两个命令letconst,用来声明变量,有关它们完整的概念我会在《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 小结
  1. 变量提升:函数声明和变量声明总是会被解释器悄悄地被"提升"到方法体的最顶部,但变量的初始化不会提升;
  2. 函数提升:函数声明可以被看作是函数的整体被提升到了代码的顶部,但函数字面量表达式并不会引发函数提升;
  3. 函数提升优先与变量提升;
  4. let和const可以有效的规避变量提升

最后提炼一下:JavaScript引擎并不总是按照代码的顺序来进行解析。在编译阶段,无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理,这个过程被称为提升。声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升。

相关免费学习推荐:javascript(视频)

以上是JavaScript專題之一:變數提升與預編譯的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除