Javascript基礎(chǔ)--執(zhí)行環(huán)境和作用域鏈

執(zhí)行環(huán)境

執(zhí)行環(huán)境:是代碼執(zhí)行時(shí)產(chǎn)生的一個(gè)上下文環(huán)境(context)腿箩。每個(gè)執(zhí)行環(huán)境都包含一個(gè)變量對(duì)象, 用來保存該執(zhí)行環(huán)境下的變量和函數(shù)沥阳。執(zhí)行環(huán)境包括全局執(zhí)行環(huán)境和函數(shù)執(zhí)行環(huán)境。
全局執(zhí)行環(huán)境:是javascript程序代碼一開始執(zhí)行時(shí)就產(chǎn)生的上下文環(huán)境往产。瀏覽器中的全局執(zhí)行環(huán)境就是window對(duì)象----全局變量和全局函數(shù)粘拾,即是window內(nèi)的屬性和方法。
函數(shù)執(zhí)行環(huán)境:當(dāng)函數(shù)代碼執(zhí)行時(shí)藻肄,會(huì)產(chǎn)生的函數(shù)執(zhí)行環(huán)境。該執(zhí)行環(huán)境下的變量對(duì)象保存函數(shù)的內(nèi)部變量和內(nèi)部函數(shù)拒担。

作用域

一般的編程語言如c++和java嘹屯,都有塊作用域({}大括號(hào)形成一個(gè)塊作用域)。而javascript語言在es6標(biāo)準(zhǔn)之前从撼,卻只有全局作用域和函數(shù)作用域州弟,沒有塊作用域。比如:

function foo() {
  {
     var a = 'hello';
  }
  console.log(a); // 打印 hello
}
foo();

由于沒有塊作用域低零,a變量能被外層調(diào)用到婆翔。

作用域鏈

作用域鏈, 執(zhí)行環(huán)境在初始化時(shí)會(huì)創(chuàng)建作用域鏈,用來標(biāo)記當(dāng)前作用域內(nèi)成員的訪問次序掏婶。它本質(zhì)是一個(gè)指針列表啃奴,最前端節(jié)點(diǎn)稱為活動(dòng)對(duì)象,等于當(dāng)前執(zhí)行環(huán)境的變量對(duì)象雄妥。第二個(gè)節(jié)點(diǎn)是外層函數(shù)的變量對(duì)象最蕾,第三個(gè)節(jié)點(diǎn)是外層函數(shù)的外層函數(shù)的節(jié)點(diǎn)...... 直到最后一個(gè)節(jié)點(diǎn),即全局執(zhí)行環(huán)境的變量對(duì)象老厌。作用域鏈瘟则,是一個(gè)很重要的概念,它是實(shí)現(xiàn)閉包的一個(gè)理論基礎(chǔ)枝秤。
下面的例子用來說明作用域鏈的工作原理

var a = 'hello';
var b = 'world';

function foo() {
    var a = 'hi';
    var bar = function() {
        console.log(a); // 打印 hi
        console.log(b); // 打印 world   
        console.log(c); // 報(bào)錯(cuò): c is not defined
    }
    return bar;
}

foo()();

當(dāng)執(zhí)行foo()時(shí)醋拧,會(huì)創(chuàng)建函數(shù)foo的作用域鏈,頭結(jié)點(diǎn)即當(dāng)前執(zhí)行環(huán)境的變量對(duì)象淀弹,包含變量a(值為hi)和內(nèi)部函數(shù)bar丹壕。
當(dāng)執(zhí)行foo()()時(shí),即執(zhí)行foo函數(shù)的內(nèi)部函數(shù)bar時(shí)垦页,也創(chuàng)建作用域鏈雀费,。當(dāng)執(zhí)行console.log(a)時(shí)痊焊,由于當(dāng)前活動(dòng)對(duì)象內(nèi)沒查找到a變量盏袄,就向作用域鏈的前一個(gè)節(jié)點(diǎn)查找忿峻,即函數(shù)foo的變量對(duì)象,發(fā)現(xiàn)存在a變量辕羽,查找結(jié)束逛尚,打印出a變量的值。
當(dāng)執(zhí)行console.log(b)時(shí)刁愿,發(fā)現(xiàn)函數(shù)foo的執(zhí)行環(huán)境的變量對(duì)象內(nèi)部也沒有b變量绰寞,繼續(xù)像前一個(gè)節(jié)點(diǎn)查找,即全局執(zhí)行環(huán)境的變量對(duì)象铣口,發(fā)現(xiàn)內(nèi)部包含b變量(即全局變量)滤钱,查找結(jié)束,打印全局變量b的值脑题。
當(dāng)執(zhí)行console.log(c)時(shí)件缸,一直查到到最外層的全局變量對(duì)象,也沒有找到c變量叔遂,查找失敗他炊,打印c沒有定義的錯(cuò)誤。

塊作用域

es6引入了let關(guān)鍵字和塊作用域已艰。
依然是上面的例子痊末,將var改成let,let修飾的a變量哩掺,只存在于自己的塊作用域中凿叠,不會(huì)被外層訪問。

function foo() {
  {
     let a = 'hello';
  }
  console.log(a); // 報(bào)錯(cuò):"a is not defined"
}
foo();

下面的代碼可能在實(shí)際開發(fā)中遇到:

function arrayFun() {
    var arr = [];
    for (var i = 0; i < 5; i++) {
        arr.push(function () {
            console.log(i);
        });
    }
    return arr;
}
var retArr = arrayFun();
for (var i = 0; i < 5; i++) {
    retArr[i]();   // 打印 5疮丛,5幔嫂,5,5誊薄,5
}

本來想要得到的結(jié)果是0,1锰茉,2呢蔫,3,4飒筑。但由于var修飾的變量i存在于函數(shù)作用域arrayFun中片吊,當(dāng)執(zhí)行完arrayFun,i的值變?yōu)?协屡,之后在調(diào)用數(shù)組arr內(nèi)的函數(shù)成員時(shí)俏脊,打印值都是5。這種問題肤晓,在es6之前爷贫,通常的解決方案是將i作為參數(shù)傳遞給匿名函數(shù):

function arrayFun() {
    var arr = [];
    for (var i = 0; i < 5; i++) {
        arr.push(function (j) {
            return function() {
                console.log(j);
            }
        }(i));
    }
    return arr;
}
var retArray = arrayFun();
for (var i = 0; i < 5; i++) {
    retArray[i]();   // 打印 0, 1, 2, 3, 4
}

在es6中认然,使用let修飾變量i,將使得i存在于快作用域下----for循環(huán)的大括號(hào):{}.

function arrayFun() {
    var arr = [];
    for (let i = 0; i < 5; i++) {
        arr.push(function () {
            console.log(i);
        });
    }
    return arr;
}
var retArr = arrayFun();
for (let i = 0; i < 5; i++) {
    retArr[i]();   // 打印 0漫萄,1卷员,2,3腾务,4
}

看毕骡,是不是比之前的代碼簡(jiǎn)潔,還更加好理解了呢岩瘦?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末未巫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子启昧,更是在濱河造成了極大的恐慌叙凡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箫津,死亡現(xiàn)場(chǎng)離奇詭異狭姨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)苏遥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門饼拍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人田炭,你說我怎么就攤上這事师抄。” “怎么了教硫?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵叨吮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我瞬矩,道長(zhǎng)茶鉴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任景用,我火速辦了婚禮涵叮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伞插。我一直安慰自己割粮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布媚污。 她就那樣靜靜地躺著舀瓢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耗美。 梳的紋絲不亂的頭發(fā)上京髓,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天航缀,我揣著相機(jī)與錄音,去河邊找鬼朵锣。 笑死谬盐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诚些。 我是一名探鬼主播飞傀,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼诬烹!你這毒婦竟也來了砸烦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤绞吁,失蹤者是張志新(化名)和其女友劉穎幢痘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體家破,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颜说,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了汰聋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片门粪。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖烹困,靈堂內(nèi)的尸體忽然破棺而出玄妈,到底是詐尸還是另有隱情,我是刑警寧澤髓梅,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布拟蜻,位于F島的核電站,受9級(jí)特大地震影響枯饿,放射性物質(zhì)發(fā)生泄漏酝锅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一奢方、第九天 我趴在偏房一處隱蔽的房頂上張望屈张。 院中可真熱鬧,春花似錦袱巨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至剖效,卻和暖如春嫉入,著一層夾襖步出監(jiān)牢的瞬間焰盗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工咒林, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留熬拒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓垫竞,卻偏偏與公主長(zhǎng)得像澎粟,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欢瞪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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