問題:以下代碼會輸出什么宅静?
showName()
console.log(myname)
var myname = 'jack'
function showName() {
console.log('函數(shù)showName被執(zhí)行');
}
console.log(myname)
如果要了解為什么會存在變量提升就需要先了解下變量存在哪里章蚣,提升是什么意思?
答案是:代碼在編譯階段姨夹,變量和函數(shù)會被存放到變量環(huán)境中究驴,變量的默認值會被設置為 undefined;在代碼執(zhí)行階段匀伏,JavaScript 引擎會從變量環(huán)境中去查找自定義的變量和函數(shù)。(就是存在變量環(huán)境中的意思蝴韭,繼續(xù)變量環(huán)境是啥)
首先javascript代碼的執(zhí)行過程是什么樣的呢够颠?一段 JavaScript 代碼在執(zhí)行之前需要被 JavaScript 引擎編譯,編譯完成之后榄鉴,才會進入執(zhí)行階段履磨。意思就是分兩個階段:編譯蛉抓,編譯之后生個什么東西,執(zhí)行剃诅。
編譯階段
從上圖可以看巷送,經(jīng)過編譯后,會生成兩部分內容:執(zhí)行上下文(Execution context)和可執(zhí)行代碼矛辕。
執(zhí)行上下文是 :JavaScript 執(zhí)行一段代碼時的運行環(huán)境笑跛。比如調用一個函數(shù),就會進入這個函數(shù)的執(zhí)行上下文聊品,在這個階段中飞蹂,執(zhí)行上下文會分別創(chuàng)建變量對象,建立作用域鏈翻屈,以及確定this指向陈哑。
以上就說明:先編譯生成執(zhí)行上下文,執(zhí)行上下文中有變量對象伸眶,變量就存在這個變量對象中
一惊窖、我們可以一行一行來分析代碼,看看編譯過程厘贼。
showName()
console.log(myname)
var myname = 'jack'
function showName() {
console.log('函數(shù)showName被執(zhí)行');
}
console.log(myname)
第 1 行和第 2 行界酒,由于這兩行代碼不是聲明操作,所以 JavaScript 引擎不會做任何處理涂臣;
第 3 行盾计,由于這行是經(jīng)過 var 聲明的,因此 JavaScript 引擎將在環(huán)境對象中創(chuàng)建一個名為 myname 的屬性赁遗,并使用 undefined 對其初始化署辉;
第 4 行,JavaScript 引擎發(fā)現(xiàn)了一個通過 function 定義的函數(shù)岩四,所以它將函數(shù)定義存儲到堆 (HEAP)中哭尝,并在環(huán)境對象中創(chuàng)建一個 showName 的屬性,然后將該屬性值指向堆中函數(shù)的位置剖煌。如果變量與函數(shù)同名材鹦,則在這個階段,以函數(shù)值為準耕姊。
經(jīng)過編譯階段變量對象中是這樣的:
// VO 為 Variable Object的縮寫桶唐,即變量對象
VO = {
myname : undefined,
showName : <showName reference> // 表示showName 的地址引用
}
執(zhí)行階段:
JavaScript 引擎開始執(zhí)行“可執(zhí)行代碼”,按照順序一行一行地執(zhí)行茉兰。
showName()
console.log(myname)
var myname = 'jack'
function showName() {
console.log('函數(shù)showName被執(zhí)行');
}
console.log(myname)
我們可以一行一行來分析上述代碼:(執(zhí)行階段):
第 1 行尤泽,調用函數(shù)showName能夠在變量對象中找到 ,輸出 '函數(shù)showName被執(zhí)行';
第 2 行坯约,查找到myname熊咽,根據(jù)編譯階段的結果,輸出 undefined闹丐;
第 3行横殴, 將myname 賦值為jack;
第 4-6行,這就是函數(shù)聲明
第7行卿拴,查找到myname衫仑,經(jīng)過第三行的操作,輸出 'jack'巍棱;
// 執(zhí)行階段
VO -> AO // Active Object
AO = {
showName : <showName reference>,
myname : 'jack',
this: Window
}
練習:
console.log(foo());
function foo() {
return 2;
}
var a;
console.log(a);
a = 1;