JavaScript進階-執(zhí)行上下文棧和變量對象(一周一更)

前言

在閱讀本篇文章之前, 請先了解執(zhí)行上下文執(zhí)行棧的基礎(chǔ)知識點, 移步《JavaScript進階-執(zhí)行上下文(理解執(zhí)行上下文一篇就夠了)》

本篇文章是接著介紹執(zhí)行上下文的要點和講解變量提升.

變量提升

在使用javascript編寫代碼的時候, 我們知道, 聲明一個變量用var, 定義一個函數(shù)用function.那你知道程序在運行它的時候, 都經(jīng)歷了什么嗎?

變量聲明提升

首先是用var定義一個變量的時候, 例如:

var a = 10;

大部分的編程語言都是先聲明變量再使用, 但是javascript有所不同, 上面的代碼, 實際相當于這樣執(zhí)行:

var a;
a = 10;

因此有了下面這段代碼的執(zhí)行結(jié)果:

console.log(a); // 聲明,先給一個默認值undefined;
var a = 10; // 賦值,對變量a賦值了10
console.log(a); // 10

上面的代碼??在第一行中并不會報錯Uncaught ReferenceError: a is not defined, 是因為聲明提升, 給了a一個默認值.

這就是最簡單的變量聲明提升.

函數(shù)聲明提升

定義函數(shù)也有兩種方法:

  • 函數(shù)聲明: function foo () {};
  • 函數(shù)表達式: var foo = function () {}.

第二種函數(shù)表達式的聲明方式更像是給一個變量foo賦值一個匿名函數(shù).

那這兩種在函數(shù)聲明的時候有什么區(qū)別嗎?

案例一??:

console.log(f1) // function f1(){}
function f1() {} // 函數(shù)聲明
console.log(f2) // undefined
var f2 = function() {} // 函數(shù)表達式

可以看到, 使用函數(shù)聲明的函數(shù)會將整個函數(shù)都提升到作用域(后面會介紹到)的最頂部, 因此打印出來的是整個函數(shù);

而使用函數(shù)表達式聲明則類似于變量聲明提升, 將var f2提升到了頂部并賦值undefined.


我們將案例一的代碼添加一點東西:

案例二??:

console.log(f1) // function f1(){...}
f1(); // 1
function f1() { // 函數(shù)聲明
    console.log('1')
}
console.log(f2) // undefined
f2(); // 報錯: Uncaught TypeError: f2 is not a function
var f2 = function() { // 函數(shù)表達式
    console.log('2')
}

雖然f1()function f1 () {...}之前,但是卻可以正常執(zhí)行;

f2()卻會報錯, 原因在案例一中也介紹了是因為在調(diào)用f2()時, f2還只是undifined并沒有被賦值為一個函數(shù), 因此會報錯.

聲明優(yōu)先級: 函數(shù)大于變量

通過上面的介紹我們已經(jīng)知道了兩種聲明提升, 但是當遇到函數(shù)和變量同名且都會被提升的情況時, 函數(shù)聲明的優(yōu)先級是要大于變量聲明的.

  • 變量聲明會被函數(shù)聲明覆蓋
  • 可以重新賦值

案例一??:

console.log(f1); // f f1() {...}
var f1 = "10";
function f1() {
  console.log('我是函數(shù)')
}
// 或者將 var f1 = "10"; 放到后面

案例一說明了變量聲明會被函數(shù)聲明所覆蓋.

案例二??:

console.log(f1); // f f1() { console.log('我是新的函數(shù)') }
var f1 = "10";

function f1() {
  console.log('我是函數(shù)')
}

function f1() {
  console.log('我是新的函數(shù)')
}

案例二說明了前面聲明的函數(shù)會被后面聲明的同名函數(shù)給覆蓋.

如果你搞懂了, 來做個小練習(xí)?

練習(xí)??

function test(arg) {
  console.log(arg);
  var arg = 10;
  function arg() {
    console.log('函數(shù)')
  }
  console.log(arg)
}
test('LinDaiDai');

答案??

function test(arg) {
  console.log(arg); // f arg() { console.log('函數(shù)') }
  var arg = 10;
  function arg() {
    console.log('函數(shù)')
  }
  console.log(arg); // 10
}
test('LinDaiDai');
  1. 函數(shù)里的形參arg被后面函數(shù)聲明arg給覆蓋了, 所以第一個打印出的是函數(shù);
  2. 當執(zhí)行到var arg = 10的時候, arg又被賦值了10, 所以第二個打印出10.

執(zhí)行上下文棧的變化

先來看看下面兩段代碼, 在執(zhí)行結(jié)果上是一樣的, 那么它們在執(zhí)行的過程中有什么不同嗎?

var scope = "global";
function checkScope () {
  var scope = "local";
  function fn () {
    return scope;
  }
  return fn();
}
checkScope();
var scope = "global"
function checkScope () {
  var scope = "local"
  function fn () {
    return scope
  }
  return fn;
}
checkScope()();

答案是 執(zhí)行上下文棧的變化不一樣。

在第一段代碼中, 棧的變化是這樣的:

ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();

可以看到fn后被推入棧中, 但是先執(zhí)行了, 所以先被推出棧;


而在第二段中, 棧的變化為:

ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();

由于checkscope是先推入棧中且先執(zhí)行的, 所以在fn被執(zhí)行前就被推出了.

VO/AO

接下來要介紹兩個概念:

  • VO(變量對象), 也就是variable object, 創(chuàng)建執(zhí)行上下文時與之關(guān)聯(lián)的會有一個變量對象抱婉,該上下文中的所有變量和函數(shù)全都保存在這個對象中方仿。

  • AO(活動對象), 也就是``activation object`,進入到一個執(zhí)行上下文時添寺,此執(zhí)行上下文中的變量和函數(shù)都可以被訪問到褪猛,可以理解為被激活了赌结。

活動對象和變量對象的區(qū)別在于:

  • 變量對象(VO)是規(guī)范上或者是JS引擎上實現(xiàn)的吭露,并不能在JS環(huán)境中直接訪問吠撮。
  • 當進入到一個執(zhí)行上下文后,這個變量對象才會被激活讲竿,所以叫活動對象(AO)泥兰,這時候活動對象上的各種屬性才能被訪問。

上面似乎說的比較難理解??, 沒關(guān)系, 我們慢慢來看.

執(zhí)行過程

首先來看看一個執(zhí)行上下文(EC) 被創(chuàng)建和執(zhí)行的過程:

  1. 創(chuàng)建階段:
  • 創(chuàng)建變量题禀、參數(shù)鞋诗、函數(shù)arguments對象;

  • 建立作用域鏈;

  • 確定this的值.

  1. 執(zhí)行階段:

變量賦值, 函數(shù)引用, 執(zhí)行代碼.

進入執(zhí)行上下文

在創(chuàng)建階段, 也就是還沒有執(zhí)行代碼之前

此時的變量對象包括(如下順序初始化):

  1. 函數(shù)的所有形參(僅在函數(shù)上下文): 沒有實參, 屬性值為undefined;
  2. 函數(shù)聲明:如果變量對象已經(jīng)存在相同名稱的屬性,則完全替換這個屬性;
  3. 變量聲明:如果變量名稱跟已經(jīng)聲明的形參或函數(shù)相同迈嘹,則變量聲明不會干擾已經(jīng)存在的這類屬性

一起來看下面的例子??:

function fn (a) {
  var b = 2;
  function c () {};
  var d = function {};
  b = 20
}
fn(1)

對于上面的例子, 此時的AO是:

AO = {
    arguments: {
        0: 1,
        length: 1
    },
    a: 1,
    b: undefined,
    c: reference to function c() {},
    d: undefined
}

可以看到, 形參arguments此時已經(jīng)有賦值了, 但是變量還是undefined.

代碼執(zhí)行

到了代碼執(zhí)行時, 會修改變量對象的值, 執(zhí)行完后AO如下:

AO = {
  arguments: {
  0: 1,
  length: 1
  },
  a: 1,
  b: 20,
  c: reference to function c() {},
  d: reference to function d() {}
}

在此階段, 前面的變量對象中的值就會被賦值了, 此時變量對象處于激活狀態(tài).

總結(jié)

  • 全局上下文的變量對象初始化是全局對象, 而函數(shù)上下文的變量對象初始化只有Arguments對象;

  • EC創(chuàng)建階段分為創(chuàng)建階段和代碼執(zhí)行階段;

  • 在進入執(zhí)行上下文時會給變量對象添加形參削彬、函數(shù)聲明全庸、變量聲明等初始的屬性值;

  • 在代碼執(zhí)行階段,會再次修改變量對象的屬性值.

后語

參考文章:

《聊一聊javascript執(zhí)行上下文》

《木易楊前端進階-JavaScript深入之執(zhí)行上下文棧和變量對象》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末融痛,一起剝皮案震驚了整個濱河市壶笼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雁刷,老刑警劉巖覆劈,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異安券,居然都是意外死亡墩崩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門侯勉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹦筹,“玉大人,你說我怎么就攤上這事址貌☆砉眨” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵练对,是天一觀的道長遍蟋。 經(jīng)常有香客問我,道長螟凭,這世上最難降的妖魔是什么虚青? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮螺男,結(jié)果婚禮上棒厘,老公的妹妹穿的比我還像新娘。我一直安慰自己下隧,他們只是感情好奢人,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著淆院,像睡著了一般何乎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上土辩,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天支救,我揣著相機與錄音,去河邊找鬼拷淘。 笑死搂妻,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的辕棚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逝嚎!你這毒婦竟也來了扁瓢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤补君,失蹤者是張志新(化名)和其女友劉穎引几,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挽铁,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡伟桅,尸身上長有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
  • 我被黑心中介騙來泰國打工冗澈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钦勘,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓亚亲,卻偏偏與公主長得像彻采,于是被迫代替她去往敵國和親腐缤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

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