1. 程式人生 > >Javascript中函數提升和變量提升

Javascript中函數提升和變量提升

mage java 之前 fun 再次 優先級 函數表達式 logs object

詞法分析

詞法分析方法:

js運行前有一個類似編譯的過程即詞法分析,詞法分析主要有三個步驟:

  • 分析參數
  • 再分析變量的聲明
  • 分析函數說明

具體步驟如下:

  • 函數在運行的瞬間,生成一個活動對象(Active Object),簡稱AO
  • 分析參數
  1. 函數接收形式參數,添加到AO的屬性,並且這個時候值為undefine,例如AO.age=undefine
  2. 接收實參,添加到AO的屬性,覆蓋之前的undefine
  • 分析變量聲明,如var age;或var age=23;
  1. 如果上一步分析參數中AO還沒有age屬性,則添加AO屬性為undefine,即AO.age=undefine
  2. 如果AO上面已經有age屬性了,則不作任何修改
  • 分析函數的聲明,如果有function age(){}

把函數賦給AO.age ,覆蓋上一步分析的值

代碼例子1

這樣我們先通過一段代碼來理解詞法分析:

技術分享

詞法分析階段:

等價於:

技術分享

  • 首先形成Active Object即AO對象
  • 第一步:分析形式參數

AO.age = undefine

傳入實參即對AO.age=undefine進行覆蓋:

AO.age = 3

  • 第二步:分析局部變量

存在var age = 27;

這個時候遵循如果AO.age存在值則不作任何修改,按照第一步分析的最後結果AO.age = 3,所以這裏不作任何修改即:

AO.age = 3

  • 第三步:分析函數的聲明,

因為函數中存在function age(){}函數

所以按照規則應該將函數賦值給AO.age覆蓋第二步分析的AO.age = 3即:

AO.age = function age(){}

執行階段

執行t1函數,到console.log(age)時,詞法分析的最後AO.age= function age(){},所以會打印:

function age(){}

var age=27;給age賦值27

到第二個console.log(age)這個時候age已經重新被賦值27,所以這個時候會打印:

27

function age() 並沒有調用所以並不會執行

到第三個console.log(age)這個時候age的值並沒有被再次修改,所以這個時候會打印:

27

運行js查看結果如下與我們分析的完全相符:

技術分享

代碼例子2

技術分享

和上面的詞法分析過程一樣

詞法分析階段:

等價於:

技術分享

  • 首先形成Active Object即AO對象
  • 第一步:分析形式參數

AO.age = undefine

傳入實參即對AO.age=undefine進行覆蓋:

AO.age = 22

  • 第二步:分析局部變量

第一步中最後得到AO.age = 22

所以這裏var age;以及var age =23 ,因為AO.age屬性已經存在值,所以這個時候遵循如果存在則不作任何修改,即:

AO.age = 22

  • 第三步:分析函數的聲明,

因為函數中存在function age(){}函數

所以按照規則應該將函數賦值給AO.age覆蓋第二步分析的AO.age = 22即:

AO.age = function age(){}

執行階段

執行t1函數,到console.log(age)時,詞法分析的最後AO.age= function age(){},所以會打印:

function age(){}

var age=23;給age賦值23

到第二個console.log(age)這個時候age已經重新被賦值23,所以這個時候會打印:

23

function age() 並沒有調用所以並不會執行

到第三個console.log(age)這個時候age的值並沒有被再次修改,所以這個時候會打印:

23

運行js查看結果如下與我們分析的完全相符:

技術分享

代碼例子3

技術分享

詞法分析階段:

等價於:

技術分享

  • 首先形成Active Object即AO對象
  • 第一步:分析形式參數

AO.age = undefine

傳入實參即對AO.age=undefine進行覆蓋:

AO.age = 22

  • 第二步:分析局部變量

第一步中最後得到AO.age = 22,所以這裏遵循,如果AO.age存在值則不作任何修改即:

AO.age = 22

  • 第三步:分析函數的聲明

因為函數中存在function age(){console.log(age)}函數

所以按照規則應該將函數賦值給AO.age覆蓋第二步分析的AO.age = 22即:

AO.age = function age(){console.log(age)}

執行階段

執行t1函數,到console.log(age)時,詞法分析的最後AO.age= function age(){console.log(age)},所以會打印:

function age(){console.log(age)}

age = 23,這個時候會覆蓋原來的function age(){console.log(age)},所以第二個console.log(age)會打印:

23

function age() 是一個函數表達式,所以不會做任何操作

age() 這個時候的age還是23,並不是函數表達式,所以這裏會報錯

運行js查看結果如下與我們分析的完全相符:

技術分享

這裏的提示錯誤確實也是說age不是一個函數

代碼例子4

技術分享

詞法分析階段:

  • 首先形成Active Object即AO對象
  • 第一步:分析形式參數

AO.age = undefine

傳入實參即對AO.age=undefine進行覆蓋:

AO.age = 23

  • 第二步:分析局部變量

第一步中最後得到AO.age = 23,所以這裏遵循,如果AO.age存在值則不作任何修改即:

AO.age = 23

  • 第三步:分析函數的聲明

因為函數中存在function age(){console.log(age)}函數

所以按照規則應該將函數賦值給AO.age覆蓋第二步分析的AO.age = 23即:

AO.age = function age(){console.log(age)}

執行階段

執行t1函數,到console.log(age)時,詞法分析的最後AO.age= function age(){console.log(age)},所以會打印:

function age(){console.log(age)}

function age() 是一個函數表達式,所以不會做任何操作

age()這個時候age是一個函數表達式,這裏會執行function age(){console.log(age)},這個時候函數裏console.log(age),age沒有被修改所以還是function age(){console.log(age)},即打印:

function age(){console.log(age)}

最後一個console.log(age)這裏的age沒有被修改還是function age(){console.log(age)},所以會打印:

function age(){console.log(age)}

運行js查看結果如下與我們分析的完全相符:

技術分享

代碼例子5:

技術分享

詞法分析階段:

  • 首先形成Active Object即AO對象
  • 第一步:分析形式參數

AO.age = undefine

傳入實參即對AO.age=undefine進行覆蓋:

AO.age = 23

  • 第二步:分析局部變量

第一步中最後得到AO.age = 23,所以這裏遵循,如果AO.age存在值則不作任何修改即:

AO.age = 23

  • 第三步:分析函數的聲明

這裏並沒有函數聲明表達式

所以最後分析的結果是:

AO.age = 23

執行階段

執行t1函數,到console.log(age)時,詞法分析的最後AO.age=23

所以第一個console.log(age)會打印

23

var age = function () {console.log(age)},這裏將var = 23進行覆蓋這個時候age是一個函數表達式

age() 正好調用function () {console.log(age)},這個時候這個函數裏的console.log(age),age並沒有修改還是一個函數表達式,所以會打印

function () {console.log(age)}

最後一個console.log(age)還是打印:

function () {console.log(age)}

運行js查看結果如下與我們分析的完全相符:

技術分享

代碼例子6:

技術分享

代碼例子6和代碼例子5的分析基本一樣,結果也是一樣:

技術分享

總結:

函數提升比變量提升優先級高!

Javascript中函數提升和變量提升