js的執(zhí)行過程
js代碼不是構(gòu)建之前進(jìn)行編譯的,而是在代碼執(zhí)行前很短的一段時間內(nèi),引擎沒有那么多的時間來進(jìn)行優(yōu)化
作用域
程序具有的基本功能之一,是能夠儲存變量,并在程序執(zhí)行時能夠找到變量的值,用來計算,涉及到變量的存儲和取值,有一套規(guī)則,就是作用域
在程序編譯的過程中,分三步
- 首先是分詞,詞法分析,比如var a=1這個語句,會被拆為var a = 2,就是將所寫的代碼拆成代碼塊
- 第二部,生成抽象語法樹,將第一步生成的代碼塊生成一個執(zhí)行的語法樹
- 第三部,將抽象語法樹生成可執(zhí)行的代碼,即將var a=2生成機(jī)器可執(zhí)行的01代碼
var a=2的執(zhí)行過程
編譯器先進(jìn)行此法分析,生成語法解析樹,接下來會生成生成引擎執(zhí)行所需要的代碼
- 假設(shè)生成的代碼邏輯:為一個變量a生成一個內(nèi)存空間,并把2賦給它
- 實(shí)際上:編譯器遇到var a這個代碼,首先會詢問作用域是否已經(jīng)有a這個變量,如果有,編譯器會忽略這個變量聲明繼續(xù)編譯,如果沒有,則作用域會在當(dāng)前作用域的集合中聲明一個變量,并命名為a
接下來編譯器為引擎生成a=2這個操作的可執(zhí)行代碼,引擎執(zhí)行過程中,會詢問當(dāng)前作用域是否存在a這個變量,如果存在,則將2賦給這個變量,如果不存在,引擎會繼續(xù)向上層作用域詢問是否存在,如果到了頂層沒找到,引擎結(jié)束查找,并拋出一個錯誤
引擎拋出的錯誤類型 - referceerror:引擎執(zhí)行RHS查找時,如果這個變量未聲明,會拋出referceerror錯誤,RHS查找一般用于取值操作,即賦值操作=右側(cè)的變量,在非嚴(yán)格模式下,進(jìn)行LHS查找,即賦值操作,引擎如果在全局作用域中沒有找到變量,則全局作用域會創(chuàng)建一個變量,并返回給引擎,并不會拋出錯誤
- typeerror :試圖對一個變量進(jìn)行不合法的操作,比如調(diào)動string的push方法等
為什么會有變量提升
變量的聲明是編譯器在編譯階段就命令作用域進(jìn)行了聲明,在生成可執(zhí)行代碼之前
eval with
with標(biāo)識新創(chuàng)建了一個傳入的對象的作用域,當(dāng)其屬性存在時,賦值操作被綁定到with聲明的對象上,當(dāng)找不到對應(yīng)的屬性時,對變量的賦值操作執(zhí)行普通的賦值操作,即變量的作用域變成with所在的作用域,同時,with代碼塊內(nèi)的var聲明的變量作用域依然為with所在的作用域,
不推薦使用eval和with
函數(shù)作用域,隱藏變量和規(guī)避沖突
- 函數(shù)表達(dá)式和函數(shù)聲明區(qū)別:函數(shù)名稱標(biāo)識符綁定的作用域不同,函數(shù)聲明標(biāo)識符可被外部作用域訪問,函數(shù)表達(dá)式僅可被用于函數(shù)內(nèi)部
- 具名函數(shù)和匿名函數(shù)
- 兩種立即執(zhí)行函數(shù)表達(dá)式的不同寫法
- try catch let const 垃圾收集
變量提升
- js代碼的執(zhí)行首先編譯器在編譯階段處理了所有的變量的聲明,然后才生成了可執(zhí)行代碼
- 函數(shù)表達(dá)式,具名函數(shù)表達(dá)式在賦值之前,函數(shù)名并不會被提升,賦值變量會進(jìn)行提升
- 函數(shù)聲明先于變量聲明