- JS代碼分為兩個階段:編譯階段和執(zhí)行階段页响;
- 編譯階段:會找到所有的聲明篓足,并用合適的作用域?qū)⑺鼈冴P(guān)聯(lián)起來,這是詞法作用域的核心內(nèi)容闰蚕,包括變量聲明(var a)和函數(shù)聲明(function a(){})在內(nèi)的所有聲明都會在代碼被執(zhí)行前的編譯階段首先被處理栈拖;過程類似于將變量聲明和函數(shù)聲明從他們在代碼中出現(xiàn)的位置被移動到執(zhí)行環(huán)境的頂部,這個過程就叫做提升没陡,只有聲明操作會被提升辱魁,賦值和邏輯操作會被留在原地等待執(zhí)行;
一诗鸭、JS是編譯性語言
???????JavaScript是腳本語言染簇、編譯性語言、解釋性執(zhí)行强岸;和JAVA锻弓、C這種編譯性語言的區(qū)別在于JS并不會像其他的編譯語言一樣進(jìn)行提前編譯,編譯過程通常是在實際執(zhí)行前進(jìn)行的蝌箍,而且也不會產(chǎn)生可移植的編譯結(jié)果青灼;
二、引擎妓盲、編譯器杂拨、作用域
- 引擎:負(fù)責(zé)整個JS程序的編譯及執(zhí)行過程;
- 編譯器:負(fù)責(zé)語法分析及代碼生成等工作悯衬;
- 作用域:收集并維護(hù)由所有聲明的標(biāo)識符(變量)組成的一系列查詢弹沽,實施一套非常嚴(yán)格的規(guī)則,確定當(dāng)前執(zhí)行的代碼對這些標(biāo)識符的訪問權(quán)限筋粗,即作用域是一個變量的“管家”策橘,用一個事先定義好的規(guī)則(詞法作用域)管理變量的查詢與訪問;
- 變量和函數(shù)在內(nèi)的所有聲明都會在當(dāng)前塊作用域內(nèi)被首先處理娜亿,即類似于提升到最前面聲明丽已,但是賦值處理操作因為是在執(zhí)行階段,因此編譯階段他們原地待命等待執(zhí)行买决;
三沛婴、變量聲明提升
// 變量聲明可以看成兩部分:聲明操作(var a)+賦值操作(a=10)
// 聲明操作會在編譯階段進(jìn)行,聲明操作會被提升到執(zhí)行環(huán)境的頂部督赤,值是undefined(未初始化)
// 賦值操作會留在原地等待執(zhí)行操作
var a = 2;
function foo() {
console.log(a); // undefined
var a = 10;
console.log(a); // 10
}
foo();
// 上面的代碼相當(dāng)于
var a = 2;
function foo() {
var a;
console.log(a); // undefined
a = 10;
console.log(a); // 10
}
foo()
四嘁灯、函數(shù)聲明提升
- 定義函數(shù)有兩種方法:函數(shù)聲明和函數(shù)表達(dá)式;
- 函數(shù)聲明:函數(shù)聲明提升會在編譯階段把聲明和函數(shù)體整體都提前到執(zhí)行環(huán)境頂部够挂,所以可以在函數(shù)聲明之前調(diào)用這個函數(shù)旁仿;
- 函數(shù)表達(dá)式:其實是變量聲明的一種藕夫,聲明操作會被提升到執(zhí)行環(huán)境頂部孽糖,并賦值undefined枯冈,賦值處理操作因為是在執(zhí)行階段,因此編譯階段他們原地待命等待執(zhí)行办悟;
// 函數(shù)聲明
foo(); // 100
function foo(){
console.log(100)
}
// 函數(shù)表達(dá)式
foo(); // TypeError: foo is not a function
var foo = function(){
console.log(100);
}
// 函數(shù)表達(dá)式的等價
var foo;
foo(); // TypeError: foo is not a function
foo = function(){
console.log(100);
}
五尘奏、控制語句
- JS中使用函數(shù)級作用域,不存在塊級作用域病蛉,所有普通塊中的聲明都會被提升到頂部炫加,所以控制語句對聲明的控制就顯得完全沒有效果;
if (false) {
var a = 10;
}
console.log(a); // undefined
// 相當(dāng)于
var a;
if (false) {
a = 10;
}
console.log(a); // undefined
- 奇怪的函數(shù)聲明:函數(shù)聲明發(fā)生在所有代碼執(zhí)行之前铺然,所以盡管a函數(shù)的定義過程寫在了if分支中俗孝,但是理論上,它是不會影響函數(shù)聲明提升的魄健,在新版本的瀏覽器中會出現(xiàn)此問題赋铝,舊版本的瀏覽器中會在控制臺中打印出100,這也提醒了我們盡量不要在控制語句中進(jìn)行聲明沽瘦,會造成很多無法預(yù)知的bug革骨;
console.log(a); // undefined
if(false){
function a(){
console.log(100);
}
}
a(); // TypeError: a is not a function 理論上應(yīng)該是100
六、函數(shù)聲明規(guī)則
- 函數(shù)優(yōu)先:
1)提升操作會優(yōu)先進(jìn)行函數(shù)的聲明析恋;
2)函數(shù)會首先被提升然后才是變量良哲,重復(fù)的變量聲明會被忽略,只剩下賦值操作助隧,多個函數(shù)聲明可以進(jìn)行覆蓋筑凫; - 聲明的順序是這樣的:
1)找到所有的函數(shù)聲明,初始化函數(shù)體并村,如有同名的函數(shù)則會進(jìn)行覆蓋漏健;
2)查找變量聲明,初始化為undefined橘霎,如果已經(jīng)存在同名的變量蔫浆,就什么也不做直接略過;
// 1
foo(); //200
function foo() {
console.log(100);
}
function foo() {
console.log(200);
}
// 2
console.log(foo); //function foo(){...}
function foo() {
console.log(200);
}
var foo = 100;
七姐叁、猿輔導(dǎo)的面試題
var x = 1,
y = z = 0;
function add(n) {
n = n + 1;
}
y = add(x);
function add(n) {
n = n + 3;
}
z = add(x);
console.log(x, y, z); // 1 undefined undefined
var x = 1,
y = z = 0;
function add(n) {
n = n + 1;
return n;
}
y = add(x);
function add(n) {
n = n + 3;
return n;
}
z = add(x);
console.log(x, y, z); // 1 4 4