首先我們看一段代碼的執(zhí)行
這段代碼的輸出是?undefined重贺, 為什么它會是?undefined ,而不是?a is not defined回懦,這就跟 JS 的運行機制有關气笙。
JS 中的 聲明 和 賦值
? ? 對于簡單的? var a = 1 聲明語句,我們可以理解為兩個步驟:
? ? ? ? 1 聲明部分: var a怯晕;
? ? ? ? 2 賦值部分: a = 1;
? ? 那么 函數的 聲明 和 賦值 又是如何的
? ? ? ? 對于 foo 函數 是一個完整的函數聲明潜圃,沒有涉及到 賦值操作; 而第二個函數 先聲明了 變量 bar 舟茶,再將 函數 賦值給 bar谭期。
再談變量提升
? ? 理解了 聲明 和 賦值,那么 什么是 變量提升
? ? ? ? 變量提升吧凉,是指在 JavaScript 代碼執(zhí)?過程中隧出,JavaScript 引擎把變量的聲明部分和函數的聲明部 分提升到代碼開頭的“行為”。變量被提升后阀捅,會給變量設置默認值胀瞪,這個默認值就是我們熟悉的 unde?ned。?
? ? 也就是說 對于 var a = 1饲鄙,可以理解為兩步:
? ? ? ? 1? var a = undefined;
? ? ? ? 2? a = 1;
? ? ? ? 注意 :?只有 聲明本身 會被提升凄诞,而 賦值或其他運行邏輯 會留在原地。
? ? 注意: 在每個作用域中忍级,都會進行提升操作帆谍,比如 在函數 作用域內 用 var 聲明的,會提升至 本作用域 的開頭轴咱。
再談 var a = 1
? ? JS 在執(zhí)行 var a = 1 這段代碼時汛蝙,變量 和 函數聲明 在代碼里的位置是不會改變的烈涮,而是在編譯 階段被JavaScript引擎放入內存中 。一段 JS 代碼在執(zhí)行前 需要被 JS 引擎 編譯患雇,編譯完成之后 才會進入 執(zhí)行階段跃脊。
? ? 一段 JS 代碼? --------->? ?編譯階段?? --------->? ?執(zhí)行階段
? ??這意味著無論作用域中的聲明出現在什么地方,都將在代碼本身被執(zhí)行前首先進行處理苛吱。 可以將這個過程形象地想象成所有的聲明(變量和函數)都會被“移動”到各自作用域的 最頂端酪术,這個過程被稱為提升。
????聲明本身會被提升翠储,而包括函數表達式的賦值在內的賦值操作并不會提升绘雁。
?編譯階段?
????????還是上面的 例子
? ? 一段 JS 代碼 經過 編譯階段后 會生成 兩部分:執(zhí)行上下文 和? 可執(zhí)行代碼。
? ? 執(zhí)行上下文 是 JS 執(zhí)行一段代碼時的 運行環(huán)境援所, 它存在 一個變量環(huán)境對象庐舟,這個對象保存了 變量提升部分的內容,比如上面例子中的 變量 a 和 函數 foo住拭。
? ? 分析 圖4 中的代碼:
? ? ? ? 1 第一行和第二行 不是聲明操作挪略, JS 引擎不處理。
? ? ? ? 2 第三行 是 一個 var 的聲明變量滔岳, JS 引擎會在環(huán)境對象中 創(chuàng)建一個 名為 a 的 屬性杠娱,并初始化值 undefined
? ? ? ? 3 第四行 是函數聲明語句, JS 引擎?會在環(huán)境對象中 創(chuàng)建一個 名為 foo的 屬性, 并將函數定義存儲到 堆中谱煤,屬性值 指向 函數所在堆的地址摊求。
? ? 這樣 就生成了?變量環(huán)境對象。
執(zhí)行階段
? ? 在執(zhí)行階段刘离, JS 引擎就會處理 執(zhí)行階段的代碼室叉,JS 引擎 就會在?變量環(huán)境對象 中 找到 所需要的值,并輸出結果硫惕。
總結
? ? JS 代碼在執(zhí)行過程中茧痕,會經過兩個階段 : 編譯階段 和 執(zhí)行階段。
? ? 在編譯階段恼除,變量和函數會被存放到 變量環(huán)境 中踪旷,變量的默認值會被設置為 unde?ned;在執(zhí)行階段缚柳,JavaScript 引擎會從變量環(huán)境中去查找?定義的變量和函數。