從最簡單的例子開始
var a = 1;
想要執(zhí)行代碼叼耙,必須要先要進行編譯授滓。JavaScript的編譯過程很短,就在執(zhí)行代碼之前借嗽。
編譯的主要三個過程:
1.分詞/詞法分析:
把字符組成的字符串分解成var、a转培、=恶导、1、浸须;惨寿、五個詞法單元邦泄。
2.解析/語法分析:
把詞法單元流[數(shù)組],解析成由元素逐級嵌套而成的代表程序語法結構的樹(AST)裂垦。
3.代碼生成:
把AST轉換為可執(zhí)行的代碼的過程稱為代碼生成虎韵。
更易理解的說
var a = 1;這樣一個簡單地程序,分為兩個聲明缸废,一段由編譯器在編譯時處理包蓝,另外一個由引擎在運行時處理。
- 第一步首先分解成詞法單元企量。
- 第二步解析成樹結構
- 第三步
1. 遇見 var a ,編譯器會詢問作用域测萎,是否已經有一個該名稱的變量
存在同一個作用域集合中,如果有届巩,就跳過該聲明硅瞧,繼續(xù)編譯。
如果沒有恕汇,就會要求作用域在當前的作用域集合中聲明一個新的變量
名字叫 a
2.接下來編譯器會生成運行時候的代碼腕唧,這些代碼被用來處理a = 2這個賦值操作。
引擎在運行的時候會查詢作用域內是否有叫做a的變量瘾英,
如果之前沒有聲明過枣接,就會去上一層作用域去查找。
最終找到的話缺谴,就為其執(zhí)行賦值操作但惶,
沒有找到的話,引擎就會拋出一個異常湿蛔。
更進一步的理解
關于變量的操作都會涉及LHS查詢和RHS查詢
當變量出現(xiàn)在賦值操作的左側時進行LHS查詢(不準確的理解)
a = 2;
我們不關心a所代表原來的值是什么膀曾,
我們只需要找到這個變量,賦值為2
當變量出現(xiàn)在賦值操作的右側的時候進行RHS查詢
console.log(a);
這里對a進行的是RHS查詢阳啥,因為我們沒有對它賦值添谊,而是取得它所代表的值。
function foo (){}察迟;//函數(shù)聲明不能理解成LHS查詢
var foo = function(){};//函數(shù)表達式可以斩狱。
異常
1.執(zhí)行RHS查詢的時候,如果在所有的嵌套的作用域都找不到所需要的變量
--報錯:ReferenceError
2.執(zhí)行LHS查詢的時候卷拘,如果找到了全局作用域下喊废,還是沒有找到,
就會在全局下創(chuàng)建這個變量栗弟,返回給引擎(非嚴格模式下)污筷。
嚴格模式下:
--報錯:ReferenceError
3.如果把RHS查詢的變量進行非法操作,比如把一個非函數(shù)類型的值
進行函數(shù)操作,那么就會報錯:
-- TypeError