成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

33 個 js 核心概念(一):函數(shù)調(diào)用棧,執(zhí)行上下文與變量對象

ZHAO_ / 2368人閱讀

摘要:最先執(zhí)行完畢的一定是最里面的函數(shù),執(zhí)行過后彈出調(diào)用棧,接著執(zhí)行上一層函數(shù),直至所有函數(shù)執(zhí)行完,調(diào)用棧清空。到這里你應(yīng)該就會明白,上面函數(shù)調(diào)用棧,就是生成了一個函數(shù)的執(zhí)行上下文。

前言 為什么會有這篇文章?

在書籍或博客上,我們經(jīng)常會看到「作用域鏈」、「閉包」、「變量提升」等概念,說明一個問題 —— 它們很重要。

但很多時候,對于這些概念,看的時候覺得自己已經(jīng)明白了,可過不了多久,再讓你說一說,可能就說不清楚了,之所以會這樣,是因為我們對于 JavaScript 這門語言的運行機制不清楚。

我相信搞明白了今天所講的內(nèi)容,會對你理解那些知識大有裨益!

函數(shù)調(diào)用棧(call stack) 1. 什么是棧?

類似 js 中的數(shù)組,棧也是用來存儲數(shù)據(jù)的一種數(shù)據(jù)結(jié)構(gòu)。他的特點是后進先出(LIFO)。

與之相對的一種數(shù)據(jù)結(jié)構(gòu)稱為隊列,隊列的特點是先進先出(FIFO)。

可以想象這樣一種場景:小明和同學(xué)們放學(xué)回家,老師讓他在排在隊伍的最前面,他們每天回家路上都要經(jīng)過一個胡同,小明每次都是第一個進入胡同,肯定也是第一個出來,這就是所謂「先進先出」。

可是有一天,小明他們走到胡同里發(fā)現(xiàn)胡同口停了一輛車,把胡同給堵死了,沒辦法,他們只能隊頭變隊尾往回撤,這時候,小明雖然最先進入胡同,卻只能最后出去,最先出去的是排在隊尾的小華,也就是「后進先出」。

2. 什么叫函數(shù)調(diào)用棧?

在 js 中函數(shù)的調(diào)用也遵照這樣以一個原則:最先調(diào)用的函數(shù)先放到調(diào)用棧中,假如這個函數(shù)內(nèi)部又調(diào)用了別的函數(shù),那么這個內(nèi)部函數(shù)就接著被放入調(diào)用棧中,直至不再有函數(shù)調(diào)用。最先執(zhí)行完畢的一定是最里面的函數(shù),執(zhí)行過后彈出調(diào)用棧,接著執(zhí)行上一層函數(shù),直至所有函數(shù)執(zhí)行完,調(diào)用棧清空。

這樣說可能會不太明白,舉個例子:

// 其他語句
function first() {
 console.log("first")
 function second() {
     console.log("second")
 }
 second();
 third();
 // 其他語句
}
//其他語句
function third() {
    console.log("third")
}
// 調(diào)用 first
first();

在上述代碼中,首先調(diào)用的是函數(shù) first, 此時 first 進入函數(shù)棧,接著在 first 中調(diào)用函數(shù) second,second 入棧,當(dāng) second 執(zhí)行完畢后,second 出棧,third 入棧,接著 third 執(zhí)行完出棧,執(zhí)行 first 其他代碼,直至 first 執(zhí)行完,函數(shù)棧清空。

執(zhí)行上下文(Execution Context) 1. 什么是執(zhí)行上下文?

js 代碼在執(zhí)行時,會進入一個執(zhí)行環(huán)境,它會形成一個作用域。這個執(zhí)行環(huán)境,便是執(zhí)行上下文。

JavaScript 主要有三種執(zhí)行環(huán)境:

全局執(zhí)行環(huán)境: 代碼開始執(zhí)行時首先進入的環(huán)境。

函數(shù)環(huán)境:函數(shù)調(diào)用時,會開始執(zhí)行函數(shù)中的代碼。

eval:不建議使用,可忽略。

2. 執(zhí)行上下文的生命周期

上面講到 js 代碼執(zhí)行時會生成一個執(zhí)行上下文。而這個執(zhí)行上下文的周期,分為兩個階段:

創(chuàng)建階段。這個階段會生成變量對象(VO),建立作用域鏈以及確定 this 的值。

執(zhí)行階段。這個階段進行變量賦值,函數(shù)引用及執(zhí)行代碼。

到這里你應(yīng)該就會明白,上面函數(shù)調(diào)用棧,就是生成了一個函數(shù)的執(zhí)行上下文。

3. 什么是執(zhí)行棧?

執(zhí)行上下文也同樣遵循函數(shù)調(diào)用棧的規(guī)則,無非就是多加了一層 —— 全局執(zhí)行上下文,函數(shù)執(zhí)行完后會跳出執(zhí)行棧,而全局執(zhí)行上下文,會在關(guān)閉瀏覽器后跳出執(zhí)行棧。

還是上面的例子,我們看一下執(zhí)行棧。

變量對象 1. 什么叫變量對象?

從上面其實可以得到答案,變量對象是 js 代碼在進入執(zhí)行上下文時,js 引擎在內(nèi)存中建立的一個對象,用來存放當(dāng)前執(zhí)行環(huán)境中的變量。

2. 變量對象(VO)的創(chuàng)建過程

變量對象的創(chuàng)建,是在執(zhí)行上下文創(chuàng)建階段,依次經(jīng)過以下三個過程:

創(chuàng)建 arguments 對象。對于函數(shù)執(zhí)行環(huán)境,首先查詢是否有傳入的實參,如果有,則會將參數(shù)名是實參值組成的鍵值對放入arguments 對象中,否則,將參數(shù)名和 undefined,組成的鍵值對放入 arguments 對象中。

  function bar(a, b, c) {
    console.log(arguments);  // [2, 4]
    console.log(arguments[2]); // undefined
  }
  bar(2,4)

檢查當(dāng)前環(huán)境中的函數(shù)聲明。當(dāng)遇到同名的函數(shù)時,后面的會覆蓋前面的。

console.log(a); // function a() {console.log("fjdsfs") }
function a() {
    console.log("24");
}
function a() {
  console.log("fjdsfs")
}

在上面的例子中,在執(zhí)行第一行代碼之前,函數(shù)聲明已經(jīng)創(chuàng)建完成,后面的對之前的聲明進行了覆蓋。

檢查當(dāng)前環(huán)境中的變量聲明并賦值為undefined當(dāng)遇到同名的函數(shù)聲明,為了避免函數(shù)被賦值為 undefined ,會忽略此聲明

console.log(a); // function a() {console.log("fjdsfs") }
console.log(b); // undefined
function a() {
  console.log("24");
}
function a() {
console.log("fjdsfs");
}
var b = "bbbbbbbb";
var a = 46;

在上例我們可以看到,在代碼之前前,a 仍舊是一個函數(shù),而 b 是 undefined。

根據(jù)以上三個步驟,對于變量提升也就知道是怎么回事了。

3. 變量對象變?yōu)榛顒訉ο?/em>

執(zhí)行上下文的第二個階段,稱為執(zhí)行階段,在此時,會進行變量賦值,函數(shù)引用并執(zhí)行其他代碼,此時,變量對象變?yōu)?strong>活動對象。

我們還是舉上面的例子:

   console.log(a); // function a() {console.log("fjdsfs") }
   console.log(b); // undefined
   function a() {
       console.log("24");
   }
   function a() {
     console.log("fjdsfs");
   }
   var b = "bbbb";
   console.log(b); // "bbbb"
   var a = 46; 
   console.log(a);  // 46
   var  b = "hahahah";
   console.log(b); // "hahah"

在上面的代碼中,代碼真正開始執(zhí)行是從第一行 console.log() 開始的,自這之前,執(zhí)行上下文是這樣的:

// 創(chuàng)建過程
EC= {
  VO: {}; // 創(chuàng)建變量對象
  scopeChain: {}; // 作用域鏈
}
VO = {
  argument: {...}; // 當(dāng)前為全局上下文,所以這個屬性值是空的
  a:  // 函數(shù) a  的引用地址
  b: undefiend  // 見上文創(chuàng)建變量對象的第三步
}

根據(jù)步驟,首先是 arguments 對象的創(chuàng)建;其次,是檢查函數(shù)的聲明,此時,函數(shù) a 聲明了兩次,后一次將覆蓋前一次;最后,是檢查變量的聲明,先聲明了變量 b,將它賦值為 undefined,接著遇到 a 的聲明,由于 a 已經(jīng)聲明為了一個函數(shù),所以,此條聲明將會被忽略。

到此,變量對象的創(chuàng)建階段完成,接下來時執(zhí)行階段,我們一步一步來。

執(zhí)行 console.log(a),我們知道,此時 a 是第二個函數(shù),所以會輸出function a() {...};

執(zhí)行 console.log(b),不出我們所料,將會輸出 undefined;

執(zhí)行賦值操作: b = "bbbb"

執(zhí)行 console.log(b) ,此時,b 已經(jīng)賦值,所以會輸出 "bbbb"

執(zhí)行賦值操作: a = 46;

執(zhí)行 console.log(a) ,此時,a 的值變?yōu)?46。

執(zhí)行賦值操作: b = "hahahah"

執(zhí)行 console.log(b), b 已經(jīng)被重新賦值,輸出 hahahah。

由上面我們可以看到,在執(zhí)行階段,變量對象是跟著代碼不斷變化的,此時,我們把變量對象成為活動對象。

執(zhí)行到最后一步時,執(zhí)行上下文變成了這樣。

// 執(zhí)行階段
EC = {
  VO = {};
  scopeChain: {};
}
 // VO ---- AO
AO = {
  argument: {...};
  a: 46;
  b: "hahahah";
  this: window;
}

以上,就是變量對象在代碼執(zhí)行前及執(zhí)行時的變化。

剛開始就說過,這部分概念將會對你理解后面的知識有很大的幫助,所以剛開始接觸的話可能會有些晦澀,建議就是認(rèn)真讀兩遍,結(jié)合后面的知識,經(jīng)?;剡^頭來看看。

最后留一道題,給大家作為練手,觀察觀察執(zhí)行上下文及變量對象的變化。

console.log(a);
console.log(b);
var a = 4;
function a() {
  console.log("我是a1");
  b(3, 5);
}
var a = function a() {
  console.log("我是a2");
  b(3, 5);
}
var b = function (m, n) {
   console.log(arguments);
   console.log("b")
}
a();
原文地址:阿木木的博客

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99823.html

相關(guān)文章

  • 33 js 核心概念(二):數(shù)據(jù)類型

    摘要:舉個例子在上面的例子可以看到,我們聲明是一個數(shù)字,但是我們在之后將的值又改成了字符串和布爾值后面會講這些類型。基本類型字符串表示一個字符串,如。因此,我們可以寫一個函數(shù),用來精確檢測類型。 showImg(https://segmentfault.com/img/remote/1460000017309509?w=850&h=572); 定義 1. 什么是數(shù)據(jù)類型? 數(shù)據(jù)類型,就是將...

    QiShare 評論0 收藏0
  • 春招季如何橫掃 Javascript 面試核心考點(基礎(chǔ)版)?

    摘要:當(dāng)前函數(shù)執(zhí)行完成后,當(dāng)前函數(shù)的執(zhí)行上下文出棧,并等待垃圾回收。作用域與作用域鏈到來有全局作用域函數(shù)作用域和塊級作用域新增。 引言 Javascript是前端面試的重點,本文重點梳理下 Javascript 中的??贾R點,然后就一些容易出現(xiàn)的題目進行解析。限于文章的篇幅,無法將知識點講解的面面俱到,本文只羅列了一些重難點,如果想要了解更多內(nèi)容歡迎點擊我的博客。 一、變量類型 1.JS ...

    impig33 評論0 收藏0
  • 春招季如何橫掃 Javascript 面試核心考點(基礎(chǔ)版)?

    摘要:當(dāng)前函數(shù)執(zhí)行完成后,當(dāng)前函數(shù)的執(zhí)行上下文出棧,并等待垃圾回收。作用域與作用域鏈到來有全局作用域函數(shù)作用域和塊級作用域新增。 引言 Javascript是前端面試的重點,本文重點梳理下 Javascript 中的??贾R點,然后就一些容易出現(xiàn)的題目進行解析。限于文章的篇幅,無法將知識點講解的面面俱到,本文只羅列了一些重難點,如果想要了解更多內(nèi)容歡迎點擊我的博客。 一、變量類型 1.JS ...

    jayce 評論0 收藏0
  • 理解JavaScript的核心知識點:This

    摘要:關(guān)鍵字計算為當(dāng)前執(zhí)行上下文的屬性的值。毫無疑問它將指向了這個前置的對象。構(gòu)造函數(shù)也是同理。嚴(yán)格模式無論調(diào)用位置,只取顯式給定的上下文綁定的,通過方法傳入的第一參數(shù),否則是。其實并不屬于特殊規(guī)則,是由于各種事件監(jiān)聽定義方式本身造成的。 this 是 JavaScript 中非常重要且使用最廣的一個關(guān)鍵字,它的值指向了一個對象的引用。這個引用的結(jié)果非常容易引起開發(fā)者的誤判,所以必須對這個關(guān)...

    TerryCai 評論0 收藏0
  • 【譯】JavaScript 核心(第二版)

    摘要:技術(shù)上來說這個機制被稱為動態(tài)分配或代理。定義類一個類是一個正式的抽象集,它規(guī)定了對象的初始狀態(tài)和行為。技術(shù)上來說一個類表示構(gòu)造函數(shù)原型的組合。因此構(gòu)造函數(shù)創(chuàng)建對象并自動設(shè)置新創(chuàng)建實例的原型。第二次調(diào)用時,相同的上下文再次被壓入棧并恢復(fù)。 原文:JavaScript. The Core: 2nd Edition作者:Dmitry Soshnikov 文章其他語言版本:俄語 這篇文章是 ...

    dingding199389 評論0 收藏0

發(fā)表評論

0條評論

ZHAO_

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<