單信js——3預(yù)編譯和函數(shù)執(zhí)行鸣戴,作用域鏈(重點)

函數(shù)的執(zhí)行順序
1.語法分析(掃描一遍,看有沒有語法錯誤之類的)
2.預(yù)編譯
3.解釋執(zhí)行(解釋一行昧廷,執(zhí)行一行)

預(yù)編譯和執(zhí)行是分不開的堪嫂,預(yù)編譯就發(fā)生在函數(shù)執(zhí)行的前一刻。預(yù)編譯完成木柬,馬上逐行解釋執(zhí)行皆串。下面講預(yù)編譯,里邊含有解釋執(zhí)行的東西眉枕。
先鋪墊幾個知識點
函數(shù)聲明整體提升
變量愚战,聲明提升
暗示全局變量,任何變量如果未經(jīng)聲明就賦值齐遵,此變量歸全局所有寂玲。一切聲明的全局變量,全是window的屬性梗摇,比如b=3;意思就是window.b=3

變量拓哟,聲明提升
var a=123相當(dāng)于,var a;a=123;變量伶授,
聲明提升断序,把var a提升了流纹,打印的話就是undefined

預(yù)編譯四部曲:
1.創(chuàng)建AO對象
2.找形參和變量聲明,將形參名和變量名作為AO屬性名违诗,增加到AO里面漱凝,并且賦值undefined
3.將實參賦值給形參
4.在函數(shù)體里找函數(shù)聲明,值賦予函數(shù)體

第一步:
生成一個活動對象(Active Object)诸迟,簡稱AO茸炒,又叫做執(zhí)行期上下文
函數(shù)在運(yùn)行前,會執(zhí)行詞法分析

第二步:
分析形參和變量聲明阵苇,如var age;或var age=25;
AO.age=undefine

第三步:
將實參賦值給形參壁公,如果實參age=30,那么AO里面也跟著變化绅项,這時候
AO.age=30

第四部:
分析函數(shù)的聲明紊册,如果有function age(){}
把函數(shù)賦給AO.age ,覆蓋上一步分析的值

下面是一個例子,詳細(xì)講述函數(shù)執(zhí)行三部曲快耿,AO執(zhí)行四部曲的
<script>
    function fn(a) {
      console.log(a);
      var a=123;  //相當(dāng)于var a并且賦值為123
      console.log(a);//第三行
      function a() {    } //這個叫函數(shù)聲明
      console.log(a);
      var b=function () {    }  //這個叫函數(shù)表達(dá)式囊陡,是b的聲明賦值
      console.log(b);
      function d() {   }  //這個叫函數(shù)聲明
    }
    fn(1);
    //首先進(jìn)行語法掃描分析,沒發(fā)現(xiàn)語法錯誤掀亥,就開始預(yù)編譯关斜,然后逐行解釋執(zhí)行
    //下面重點講預(yù)編譯四部曲
    /*1.創(chuàng)建AO對象
    2.找形參和變量聲明,將形參名和變量名作為AO屬性名铺浇,
       增加到AO里面痢畜,并且賦值undefined,這時候
    AO:{
        a:undefine,(形參是a鳍侣,變量聲明也有var a丁稀,那就只留一個)
        b: undefine,(變量聲明var a和var b)
    }
    3.將實參賦值給形參,這時候
    AO:{
        a:undefine—>1,(實參a是1倚聚,這時候a=1)
        b: undefine
    }
    4.分析函數(shù)的聲明线衫,如果有同名的就覆蓋,因為函數(shù)聲明優(yōu)先級最高惑折,這時候
    AO:{
        a:undefine—>1—>function a() {    },(有同名的函數(shù)聲明授账,就覆蓋)
        b: undefine,(b不變)
        d:function d() {   } (增加了一個d惨驶,屬于函數(shù)聲明)
    }
    下面進(jìn)入解釋一行白热,執(zhí)行一行的階段,已經(jīng)看過的就不再看了(比如var a)
    第一行:console.log(a);從AO里邊找粗卜,打印出函數(shù)體
    第二行屋确,var a已經(jīng)不看了,只剩下賦值123了,這時候AO有變化
    AO:{
        a:undefine—>1—>function a() {  }—>123,
        b: undefine攻臀,
        d:function d() {   }
    }
    第三行:console.log(a)打印出123
    第四行:也不看了焕数,之前已經(jīng)提升過了
    第五行:console.log(a)也打印出123
    第六行:var b就不看了,只是把b賦值為函數(shù)體刨啸,這時候又修改AO
    AO:{
        a:undefine—>1—>function a() {  }—>123,
        b: undefine—>function () {    }堡赔,
        d:function d() {   }
    }
    第七行:console.log(b);打印出函數(shù)體function () {    }
    第八行:不看了,之前提升過了设联。
    視頻教程可以參考渡一教育遞歸善已,預(yù)編譯(上)
    */
</script>

預(yù)編譯不僅發(fā)生在函數(shù)內(nèi),也發(fā)生在全局

下面是單信老師課件部分:

  • 編譯原理
    軟件開發(fā)語言的編譯模式分為:
    編譯型編程語言:代碼開發(fā)好以后仑荐,執(zhí)行編譯命令雕拼,將編譯的文件下載到電腦上安裝即可使用
    如:c語言, c++, Java....
    解釋型編程語言:代碼開發(fā)好以后纵东,不執(zhí)行編譯粘招。 在執(zhí)行代碼時才進(jìn)行編譯,解釋一句執(zhí)行一句
    如:JS php jsp asp .net ...
    js執(zhí)行兩個階段:
    預(yù)編譯階段:將函數(shù)和變量定義進(jìn)行提升(將函數(shù)和變量的定義提到作用域的第一句之前執(zhí)行)偎球。 掃描上下文環(huán)境洒扎,如果有語法錯誤有報錯。
    解釋執(zhí)行階段:一句一句執(zhí)行

作用域:
根據(jù)上下文環(huán)境(函數(shù)會產(chǎn)生獨(dú)立的運(yùn)行環(huán)境)衰絮,將作用域劃分為:

  1. 全局作用域
    函數(shù)外定義的所有內(nèi)容(函數(shù)袍冷、變量)都是全局作用域,在任何地方都可以使用

  2. 局部作用域
    函數(shù)內(nèi)定義的內(nèi)容都是局部作用域猫牡,只能在函數(shù)內(nèi)部使用

  3. 塊級作用域
    在ES5中沒有塊級作用域的概念胡诗,可以使用IIFE(立即執(zhí)行函數(shù)表達(dá)式)實現(xiàn)塊級作用域
    在ES6中,可以使用 let實現(xiàn)塊級作用域

//全局作用域
var a='hello222'; //對于全局變量來說淌友,要不要var關(guān)鍵字都可以煌恢,a就是window的屬性
var fn=function(){
//局部作用域
var b="world";
};
fn();

看下面一個例子,求1-100的和震庭,說為什么要引入塊級作用域
var sum=0;
for(var i=1; i<=100; i++){
sum+=i;
}
console.log(sum); //這是我想要的結(jié)果瑰抵,但也附帶的出現(xiàn)了i這個全局變量污染
console.log(i); //但計算過程中的i,由于沒塊級作用域器联,變成一個全局變量二汛。 產(chǎn)生全局污染。
//總結(jié)拨拓,這樣的計算方式會產(chǎn)生全局污染

//改進(jìn)版:將運(yùn)算代碼放在函數(shù)中肴颊,避免全局污染。必須返回一個值作為最終計算結(jié)果供全局使用
//IIFE:立即執(zhí)行函數(shù)表達(dá)式
var sum=function(){
var sum=0;
for(var i=1; i<=100; i++){
sum+=i;
}
return sum;
}();
//這樣寫的話渣磷,就增加了一個函數(shù)名苫昌,仍然有可能造成全局污染,也不是最好的處理辦法。

//經(jīng)典IIFE的寫法祟身,再次改進(jìn)
(function(){
var sum=0;
for(var i=1; i<=100; i++){
sum+=i;
}
window.sum=sum;
})();
//如果直接定義函數(shù)奥务,就必須寫函數(shù)名。想不寫函數(shù)名就必須把函數(shù)定義變成函數(shù)表達(dá)式: + - 袜硫! ()氯葬,這里就變成了一個立即執(zhí)行函數(shù)表達(dá)式,最好是前面再加個分號 ;
//這樣處理后就只得到一個sum婉陷,并沒有產(chǎn)生全局i和函數(shù)名污染
為了更加方便的解決這個問題帚称,引入了塊級作用域let,不會形成變量提升

//ES6可以直接使用let實現(xiàn)塊級作用域
var sum=0;
for(let i=1; i<=100; i++){
sum+=i;
}
console.log(sum);
console.log(i);
 //使用ES6的let定義塊級作用域秽澳,不會造成全局污染

作用域鏈:

函數(shù)操作時闯睹,先查找內(nèi)部的數(shù)據(jù),找不到再去找上一級作用域的數(shù)據(jù)担神,再找不到再上一級楼吃。形成一個查詢數(shù)據(jù)的作用域鏈條,就叫作用域鏈妄讯。

IIFE 立即執(zhí)行函數(shù)表達(dá)式:

1 什么是IIFE孩锡?
IIFE: Immediately Invoked Function Expression,意為立即調(diào)用的函數(shù)表達(dá)式,也就是說,聲明函數(shù)的同時立即調(diào)用這個函數(shù)。
定義函數(shù)后立即執(zhí)行一次亥贸,沒有函數(shù)名躬窜,無法再次調(diào)用,也不會造成全局染污炕置。
2 IIFE有什么用荣挨?
可以用于解決運(yùn)算過程中產(chǎn)生的全局污染問題
也是ES5中實現(xiàn)塊級作用域的方式
3 IIFE的特點
將所有運(yùn)算代碼都放在匿名函數(shù)中進(jìn)行運(yùn)算
函數(shù)是一個表達(dá)式,沒有函數(shù)名朴摊,必須立即執(zhí)行一次
執(zhí)行一次以后就消失默垄,將所有過程產(chǎn)生的數(shù)據(jù)和變量一起銷毀,不會造成全局污染
4 IIFE的寫法
(function(){
...
window.a=result
})();
下面還是那個求和的例子仍劈,講如何使用IIFE和產(chǎn)生的效果
//正常運(yùn)算1到100的累加厕倍,想要的是運(yùn)算結(jié)果,中間過程不管
var sum=0;
for(var i=1;i<=100;i++){
sum+=i;
}
console.log(sum); //這個結(jié)果是想要的
console.log(i); //運(yùn)算完以后i會一直遺留在全局上贩疙,造成全局污染

//使用IIFE進(jìn)行改進(jìn)
var s=(function(){
var sum=0;
for(var i=1;i<=100;i++){
sum+=i;
}
return sum; //函數(shù)內(nèi)計算的結(jié)果必須返回出去
})();

//另外一種用法
(function(){
var sum=0;
for(var i=1;i<=100;i++){
sum+=i;
}
window.s=sum; //函數(shù)內(nèi)計算的結(jié)果必須返回出去讹弯,不寫return,而是把結(jié)果賦值給window的屬性s这溅,這個是閉包的雛形了组民。
})();
console.log(s);

階乘

階乘是函數(shù)的一種形式,函數(shù)內(nèi)部再次調(diào)用函數(shù)自身
它必須有退出循環(huán)執(zhí)行的條件悲靴,必須有進(jìn)入執(zhí)行的條件臭胜,
經(jīng)典寫法如下,求5的階乘,就是54321
function fn(n) {
if(n===1){
return 1
}
return n*arguments.callee(n-1)
}
console.log(fn(10));
arguments.callee代替fn函數(shù)自身

閉包:

  1. 什么是閉包耸三?
    閉包是一種作用域的體現(xiàn)乱陡,正常情況下函數(shù)內(nèi)部可以訪問函數(shù)外部的數(shù)據(jù),而函數(shù)外部不能訪問函數(shù)內(nèi)部的數(shù)據(jù)仪壮;
    可以通過特殊寫法(閉包):將函數(shù)內(nèi)的子函數(shù)暴露到全局上憨颠,可以在外部調(diào)用并且可以訪問函數(shù)內(nèi)的數(shù)據(jù)。閉包可以讓全局訪問函數(shù)內(nèi)的數(shù)據(jù)积锅。
  2. 閉包的作用
    實現(xiàn)外部可以訪問函數(shù)內(nèi)部的數(shù)據(jù)
  3. 閉包的特征
    閉包一定是函數(shù)內(nèi)嵌套函數(shù)
    閉包是一種作用域的體現(xiàn)爽彤,體現(xiàn)內(nèi)部可以訪問外部的數(shù)據(jù),而正常情況下外部不能訪問內(nèi)部的數(shù)據(jù)
    閉包可以實現(xiàn)外部訪問內(nèi)部函數(shù)缚陷,內(nèi)部函數(shù)訪問上一級函數(shù)的數(shù)據(jù)适篙。實現(xiàn)外部訪問內(nèi)部數(shù)據(jù)。
    閉包是IIFE的寫法箫爷,由于內(nèi)部數(shù)據(jù)被全局所調(diào)用嚷节,延緩資源回收。
  4. 閉包的寫法
  5. 閉包會導(dǎo)致內(nèi)存無法進(jìn)行回收
//閉包的寫法,第一種
(function(){
      var i=1;
      window.show=function(){
      return i++;
  }
})();

//閉包的寫法,第二種
var count=(function(){
     var i=1;
     return function(){
     return i++;
  }
})();
閉包

當(dāng)JavaScript引擎解析腳本時蝶缀,分為“預(yù)編譯”和“解釋執(zhí)行”兩個階段丹喻。
1薄货、“預(yù)編譯”階段翁都。首先會創(chuàng)建一個環(huán)境的上下文對象,然后把使用var聲明的變量谅猾,作為上下文對象的屬性柄慰,以undefined先行初始化;使用function關(guān)鍵字聲明的函數(shù)税娜,也作為上下文對象的屬性坐搔,定義出來,而函數(shù)則保留了定義的內(nèi)容敬矩。----在這個過程中概行,函數(shù)定義的優(yōu)先級 高于 變量定義。
2弧岳、“解釋執(zhí)行”階段凳忙。遇到變量解析,會先在當(dāng)前上下文對象中找禽炬,如果沒找到并且當(dāng)前上下文對象有prototype屬性涧卵,則去原型鏈中找,否則將會去作用域鏈中找腹尖。

在js中柳恐,執(zhí)行環(huán)境(execution context)是一個非常重要的概念。
程序代碼運(yùn)行時,會產(chǎn)生一個專門用來管理所需的變量和函數(shù)的對象---環(huán)境對象乐设,這個對象我們不能通過代碼編寫操作他讼庇,但解析器在處理數(shù)據(jù)時會在后臺使用他。
1近尚、全局環(huán)境就是最外圍的一個執(zhí)行環(huán)境巫俺,可以在代碼任意位置訪問到。在瀏覽器中肿男,全局環(huán)境就是window對象(因此介汹,所有的全局變量和函數(shù),都是window對象上的屬性和方法)舶沛。
2嘹承、局部環(huán)境以函數(shù)為主。每個函數(shù)都有自己獨(dú)立的執(zhí)行環(huán)境如庭。
局部作用域一般只在固定的代碼片段內(nèi)可訪問到叹卷,最常見的就是函數(shù)內(nèi)部,所以在很多地方就會有人把它稱為函數(shù)作用域坪它。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骤竹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子往毡,更是在濱河造成了極大的恐慌蒙揣,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件开瞭,死亡現(xiàn)場離奇詭異懒震,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嗤详,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門个扰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人葱色,你說我怎么就攤上這事递宅。” “怎么了苍狰?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵办龄,是天一觀的道長。 經(jīng)常有香客問我舞痰,道長土榴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任响牛,我火速辦了婚禮玷禽,結(jié)果婚禮上赫段,老公的妹妹穿的比我還像新娘。我一直安慰自己矢赁,他們只是感情好糯笙,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撩银,像睡著了一般给涕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上额获,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天够庙,我揣著相機(jī)與錄音,去河邊找鬼抄邀。 笑死耘眨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的境肾。 我是一名探鬼主播剔难,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奥喻!你這毒婦竟也來了偶宫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤环鲤,失蹤者是張志新(化名)和其女友劉穎纯趋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體楔绞,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡结闸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年唇兑,在試婚紗的時候發(fā)現(xiàn)自己被綠了酒朵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡扎附,死狀恐怖蔫耽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情留夜,我是刑警寧澤匙铡,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站碍粥,受9級特大地震影響鳖眼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嚼摩,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一钦讳、第九天 我趴在偏房一處隱蔽的房頂上張望矿瘦。 院中可真熱鬧,春花似錦愿卒、人聲如沸缚去。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽易结。三九已至,卻和暖如春柜候,著一層夾襖步出監(jiān)牢的瞬間搞动,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工渣刷, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留滋尉,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓飞主,卻偏偏與公主長得像狮惜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子碌识,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容