深刻淺出JS - 變量提高(函數聲明提高)

前言

在咱們的平常工做中,變量無處不在。更加深刻的去了解它,可以使得本身的JS水平更上一層樓, 從變量提高這個小知識點着手,讓咱們一塊兒來深刻了解JS吧!
html

變量提高的小栗子

console.log(a) // undefined
var a = 'hello JS' 

/* 在咱們聲明a以前爲何輸出a不會報錯呢? 不急,讓咱們接着往下看 */

num = 6;
num++;
var num;
console.log(num) // 7 好奇怪,爲何給一個尚未聲明的變量賦值會不報錯呢

function hoistFunction() {
    foo();
    function foo() {        
        console.log('running...')    
    }
}
hoistFunction(); // running... 

/* 最後一個栗子 */

alert(a) //  function a { alert(10) }
a(); // 10
var a = 3;
function a() {    
    alert(10)
};
alert(a) // 3
a = 6;
a(); // throw error複製代碼


分析緣由

JS引擎會在正式執行代碼以前進行一次」預編譯「,預編譯簡單理解就是在內存中開闢一些空間,存放一些變量和函數。具體步驟以下(browser):segmentfault

  • 頁面建立GO全局對象(Global Object)對象(window對象)。
  • 加載第一個腳本文件
  • 腳本加載完畢後,進行語法分析。
  • 開始預編譯
    • 查找函數聲明,做爲GO屬性,值賦予函數體(函數聲明優先)
    • 查找變量聲明,做爲GO屬性,值賦予undefined
    • GO/window = {
          //頁面加載建立GO同時,建立了document、navigator、screen等等屬性,此處省略
          a: undefined,
          c: undefined,
          b: function(y){
              var x = 1;
              console.log('so easy');
          }
      }複製代碼
  • 解釋執行代碼(直到執行函數b,該部分也被叫作詞法分析
    • 建立AO活動對象(Active Object)
    • 查找形參和變量聲明,值賦予undefined
    • 實參值賦給形參
    • 查找函數聲明,值賦給函數體
    • 解釋執行函數中的代碼

      GO/window = {
          //變量隨着執行流獲得初始化
          a: 1,
          c: function(){
              //...
          },
          b: function(y){
              var x = 1;
              console.log('so easy');
          }
      }
      複製代碼
  • 第一個腳本文件執行完畢,加載第二個腳本文件
  • 第二個文件加載完畢後,進行語法分析
  • 開始預編譯
    • 重複預編譯步驟 ....

預解析機制使得變量提高(Hoisting),從字面上理解就是變量和函數的聲明會移動到移動到函數或者全局代碼的開頭位置。咱們再來分析一下小栗子加深一下理解。bash

console.log(a) // 執行以前,變量提高做爲window的屬性, 值被設置爲undefined
var a = 'hello JS' 

/* JavaScript 僅提高聲明,而不提高初始化 */

num = 6;
num++;
var num;
console.log(num) // 變量提高 值爲undefined的num賦值爲6,再自增 => 7

function hoistFunction() {
    foo();
    function foo() {        
        console.log('running...')    
    }
}
hoistFunction(); // 函數聲明提高,能夠在函數體以前執行

/* 最後一個栗子 */

alert(a) // 最後的聲明爲函數聲明, 所以a此時爲函數體
a(); // 執行 a 函數,輸出10
var a = 3; // 3 賦給a
function a() {    
    alert(10)
};
alert(a) // 3
a = 6; // 6賦給a,不是一個函數,故下方執行throw error
a(); // throw error
複製代碼

注: JS並不存在真正的預編譯,var與function的提高實際是在語法分析階段就處理好的。並且JS的預編譯是以一個腳本文件爲塊的。一個腳本文件進行一次預編譯,而不是全文編譯完成再進行」預編譯」的。函數


最佳實踐 

理解了變量提高和函數提高能夠使得咱們在JS上走的更遠,可是咱們在開發中,不該該使用這一特性,而是要規範咱們的代碼,作到可維護性和可讀性。不管是變量仍是函數,都必須先聲明後使用。PS:在開發中應該使用let來約束變量提高。
spa


參考資料:  code

https://developer.mozilla.org/zh-CN/docs/Glossary/Hoisting https://www.cnblogs.com/liuhe688/p/5891273.html http://dmitrysoshnikov.com/notes/note-4-two-words-about-hoisting/ https://segmentfault.com/a/1190000010187653htm

相關文章
相關標籤/搜索