1.作用域鏈
作用域是針對(duì)變量的,比如我們創(chuàng)建了一個(gè)函數(shù),函數(shù)里面又包含了一個(gè)函數(shù),那么現(xiàn)在就有三個(gè)作用域
全局作用域==>函數(shù)1作用域==>函數(shù)2作用域
作用域的特點(diǎn)就是,先在自己的變量范圍中查找,如果找不到,就會(huì)沿著作用域往上找。
2.原型鏈
原型鏈?zhǔn)轻槍?duì)構(gòu)造函數(shù)的,比如我先創(chuàng)建了一個(gè)函數(shù),然后通過一個(gè)變量new了這個(gè)函數(shù),那么這個(gè)被new出來的函數(shù)就會(huì)繼承創(chuàng)建出來的那個(gè)函數(shù)的屬性,然后如果我訪問new出來的這個(gè)函數(shù)的某個(gè)屬性,但是我并沒有在這個(gè)new出來的函數(shù)中定義這個(gè)變量,那么它就會(huì)往上(向創(chuàng)建出它的函數(shù)中)查找,這個(gè)查找的過程就叫做原型鏈。
Object ==> 構(gòu)造函數(shù)1 ==> 構(gòu)造函數(shù)2
就和css中的繼承一樣,如果自身沒有定義就會(huì)繼承父元素的樣式。
自由變量:在當(dāng)前作用域中存在但未在當(dāng)前作用域中聲明的變量叫自由變量
【執(zhí)行環(huán)境】
執(zhí)行環(huán)境(execution context),有時(shí)也稱為執(zhí)行上下文、執(zhí)行上下文環(huán)境或環(huán)境,定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù)。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象(variable object),環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中
一定要區(qū)分執(zhí)行環(huán)境和變量對(duì)象。執(zhí)行環(huán)境會(huì)隨著函數(shù)的調(diào)用和返回,不斷的重建和銷毀。但變量對(duì)象在有變量引用(如閉包)的情況下,將留在內(nèi)存中不被銷毀

這是例子中的代碼執(zhí)行到第15行時(shí)fn(0)函數(shù)的執(zhí)行環(huán)境,執(zhí)行環(huán)境里的變量對(duì)象保存了fn()函數(shù)作用域內(nèi)所有的變量和函數(shù)的值

【執(zhí)行流】
代碼的執(zhí)行順序叫做執(zhí)行流,程序源代碼并不是按照代碼的書寫順序一行一行往下執(zhí)行,而是和函數(shù)的調(diào)用順序有關(guān)
例子中的執(zhí)行流是第1行 -> 第2行 -> 第4行 -> 第15行 -> 第5行 -> 第7行
-> 第12行 -> 第8行 -> 第9行 -> 第10行 -> 第11行 -> 第13行 ->
第8行 -> 第9行 -> 第10行 -> 第11行 -> 第14行

[注意]在程序代碼執(zhí)行之前存在著編譯和聲明提升(hoisting)的過程,本例中假設(shè)代碼是已經(jīng)經(jīng)過聲明提升過程之后的代碼
【執(zhí)行環(huán)境棧】
執(zhí)行環(huán)境棧類似于作用域鏈,有序地保存著當(dāng)前程序中存在的執(zhí)行環(huán)境。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí),函數(shù)的環(huán)境就會(huì)被推入一個(gè)環(huán)境棧中。而在函數(shù)執(zhí)行之后,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境。javascript程序中的執(zhí)行流正是由這個(gè)機(jī)制控制著
在例子中,當(dāng)執(zhí)行流進(jìn)入bar(20)函數(shù)時(shí),當(dāng)前程序的執(zhí)行環(huán)境棧如下圖所示,其中黃色的bar(20)執(zhí)行環(huán)境表示當(dāng)前程序正處此執(zhí)行環(huán)境中

當(dāng)bar(20)函數(shù)執(zhí)行完成后,當(dāng)前程序的執(zhí)行環(huán)境棧如下圖所示,bar(20)函數(shù)的執(zhí)行環(huán)境被銷毀,等待垃圾回收,控制權(quán)交還給黃色背景的fn(0)執(zhí)行環(huán)境

說明
下面按照代碼執(zhí)行流的順序?qū)υ搱D示進(jìn)行詳細(xì)說明

【1】代碼執(zhí)行流進(jìn)入全局執(zhí)行環(huán)境,并對(duì)全局執(zhí)行環(huán)境中的代碼進(jìn)入聲明提升(hoisting)

【2】執(zhí)行流執(zhí)行第1行代碼var a = 1;,對(duì)a進(jìn)行LHS查詢,給a賦值1;執(zhí)行流執(zhí)行第2行代碼var b = 2;,對(duì)b進(jìn)行LHS查詢,給b賦值2

【3】執(zhí)行流執(zhí)行第15行代碼fn(0);,調(diào)用fn(0)函數(shù),此時(shí)執(zhí)行流進(jìn)入fn(0)函數(shù)執(zhí)行環(huán)境中,對(duì)該執(zhí)行環(huán)境中的代碼進(jìn)行聲明提升過程,并將實(shí)參0賦值給形參x中。此時(shí)執(zhí)行環(huán)境棧中存在兩個(gè)執(zhí)行環(huán)境,fn(0)函數(shù)為當(dāng)前執(zhí)行流所在執(zhí)行環(huán)境

【4】執(zhí)行流執(zhí)行第5行代碼var a = 10;,對(duì)a進(jìn)行LHS查詢,給a賦值10

【5】執(zhí)行流執(zhí)行第12行代碼bar(20);,調(diào)用bar(20)函數(shù),此時(shí)執(zhí)行流進(jìn)入bar(20)函數(shù)執(zhí)行環(huán)境中,對(duì)該執(zhí)行環(huán)境中的代碼進(jìn)行聲明提升過程,并將實(shí)參20賦值給形參x中。此時(shí)執(zhí)行環(huán)境棧中存在三個(gè)執(zhí)行環(huán)境,bar(20)函數(shù)為當(dāng)前執(zhí)行流所在執(zhí)行環(huán)境
在聲明提升的過程中,由于b是個(gè)自由變量,需要通過bar()函數(shù)的作用域鏈bar() -> fn() -> 全局作用域進(jìn)行查找,最終在全局作用域中也就是代碼第2行找到var b = 2;,然后在全局執(zhí)行環(huán)境中找到b的值是2,所以給b賦值2

【6】執(zhí)行流執(zhí)行第8行代碼var a = 100;,給a賦值100;執(zhí)行流執(zhí)行第9行b = x + a;,對(duì)x進(jìn)行RHS查詢,找到x的值是20,對(duì)a進(jìn)行RHS查詢,找到a的值是100,所以通過計(jì)算b的值是120,給b賦值120;執(zhí)行第10行代碼return b;,對(duì)b進(jìn)行RHS查詢,找到b的值是120,所以函數(shù)返回值為120

【7】執(zhí)行流執(zhí)行完第10行代碼后,bar(20)的執(zhí)行環(huán)境被彈出執(zhí)行環(huán)境棧,并被銷毀,等待垃圾回收,控制權(quán)交還給fn(0)函數(shù)的執(zhí)行環(huán)境

【8】執(zhí)行流執(zhí)行第13行代碼bar(200);,調(diào)用bar(200)函數(shù),此時(shí)執(zhí)行流進(jìn)入bar(200)函數(shù)執(zhí)行環(huán)境中,對(duì)該執(zhí)行環(huán)境中的代碼進(jìn)行聲明提升過程,并將實(shí)參200賦值給形參x中。此時(shí)執(zhí)行環(huán)境棧中存在三個(gè)執(zhí)行環(huán)境,bar(200)函數(shù)為當(dāng)前執(zhí)行流所在執(zhí)行環(huán)境
與第5步相同,在聲明提升的過程中,由于b是個(gè)自由變量,需要通過bar()函數(shù)的作用域鏈bar() -> fn() -> 全局作用域進(jìn)行查找,最終在全局作用域中也就是代碼第2行找到更新后的var b = 120,然后在全局執(zhí)行環(huán)境中找到b的值是120,所以給b賦值120

【9】與第6步相同,執(zhí)行流執(zhí)行第8行代碼var a = 100;,給a賦值100;執(zhí)行流執(zhí)行第9行b = x + a;,對(duì)x進(jìn)行RHS查詢,找到x的值是200,對(duì)a進(jìn)行RHS查詢,找到a的值是100,所以通過計(jì)算b的值是300,給b賦值300;執(zhí)行第10行代碼return b;,對(duì)b進(jìn)行RHS查詢,找到b的值是300,所以函數(shù)返回值為300

【10】執(zhí)行流執(zhí)行完第10行代碼后,bar(200)的執(zhí)行環(huán)境被彈出執(zhí)行環(huán)境棧,并被銷毀,等待垃圾回收,控制權(quán)交還給fn(0)函數(shù)的執(zhí)行環(huán)境

【11】執(zhí)行流執(zhí)行第14行代碼},fn(0)的執(zhí)行環(huán)境被彈出執(zhí)行環(huán)境棧,并被銷毀,等待垃圾回收,控制權(quán)交還給全局執(zhí)行環(huán)境

【12】當(dāng)頁面關(guān)閉時(shí),全局執(zhí)行環(huán)境被銷毀,頁面再無執(zhí)行環(huán)境

總結(jié)
【1】javascript使用的是詞法作用域(另外一個(gè)為動(dòng)態(tài)作用域)。對(duì)于函數(shù)來說,詞法作用域是在函數(shù)定義時(shí)就已經(jīng)確定了,與函數(shù)是否被調(diào)用無關(guān)。通過作用域,可以知道作用域范圍內(nèi)的變量和函數(shù)有哪些,卻不知道變量的值是什么。所以作用域是靜態(tài)的
[注意]通過eval()函數(shù)和with語句可以對(duì)作用域進(jìn)行動(dòng)態(tài)修改
【2】對(duì)于函數(shù)來說,執(zhí)行環(huán)境是在函數(shù)調(diào)用時(shí)確定的,執(zhí)行環(huán)境包含作用域內(nèi)所有變量和函數(shù)的值。在同一作用域下,不同的調(diào)用(如傳遞不同的參數(shù))會(huì)產(chǎn)生不同的執(zhí)行環(huán)境,從而產(chǎn)生不同的變量的值。所以執(zhí)行環(huán)境是動(dòng)態(tài)的