成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

深入 Javascript 執(zhí)行環(huán)境

levius / 1031人閱讀

摘要:中的執(zhí)行環(huán)境與堆疊在這篇筆記中我將會深入的探討底層中的一些觀念,其中最重要的就是執(zhí)行環(huán)境。其他執(zhí)行環(huán)境都可以存取全域的東西。在這個階段直譯器會建立,透過掃描函式傳入的參數(shù),內(nèi)部的函式宣告,變數(shù)宣告。

Javascript 中的執(zhí)行環(huán)境與堆疊

在這篇筆記中我將會深入的探討 JS 底層中的一些觀念,其中最重要的就是執(zhí)行環(huán)境(Execution Context)。當(dāng)您閱讀完這篇文章後您可能會比較清楚關(guān)於直譯器的運作方式,明白為什麼有些 函式 變數(shù) 可以在他們被宣告之前就拿來使用,以及這些值是怎麼決定的。

什麼是執(zhí)行環(huán)境?

我們說當(dāng) JS 開始執(zhí)行的時候,這段程式碼必須被執(zhí)行在下面三種環(huán)境之一。

全域 Global:預(yù)設(shè)當(dāng)您程式開始執(zhí)行時的環(huán)境

函式:當(dāng)我們進入一個函式 function 時的環(huán)境,也就是開始跑函式內(nèi)部程式碼的時候

Eval:把一串字串,當(dāng)作指令來執(zhí)行時的環(huán)境

也就是說一段 JS 程式碼只能存在在上面這三種狀態(tài)或類型。

讓我們直接來看看程式碼

// Global context, JS 最外層的程式碼部分屬於全域
var greeting = "Hi";

function person() {
  // 從大括號開始到結(jié)束進入另外一個執(zhí)行環(huán)境
  var _firstName = "andy";
  var _lastName = "you";

  function firstName() {
    // 另外一個執(zhí)行環(huán)境
    return _firstName;
  }

  function lastName() {
    // 執(zhí)行環(huán)境
    return _lastName;
  }

  alert(greeting + firstName() + " " + lastName());

}

上面這段範(fàn)例沒什麼特別的,我們就是有了一個全域的執(zhí)行環(huán)境即 global context ,和 3 個 function context,唯一稍微要注意的是 global context 只會有一個。其他執(zhí)行環(huán)境都可以存取全域的東西。

當(dāng)然您可以有多個 function context 每一個 function 執(zhí)行的時候就會建立一個新的 context ,OK!不管講執(zhí)行環(huán)境或者 context 都好抽象,那我們就先把他們當(dāng)作是一個 context 物件,那這個 context 物件講白了就是表示一個環(huán)境,一個範(fàn)圍,一個狀態(tài)。它會建立一個範(fàn)圍一個自己特有的領(lǐng)域,任何在 function 裡面宣告的變數(shù)或其他東西都不能被外面直接存取。

如果這樣還不能理解,那我們換個角度來想這件事,你把 context 當(dāng)成是一張記錄表格,當(dāng)我開始在 global 執(zhí)行程式碼的時候。
任何變數(shù),function 都會被記載 global 表 上,但是當(dāng)執(zhí)行到 function 內(nèi)部的時候,此時會在開出另外一張 function 表 負責(zé)記錄 function 內(nèi)部的變數(shù)等等。

不過我個人認為 執(zhí)行環(huán)境 是最貼切的翻譯,當(dāng)我在全域這個環(huán)境時我能夠取得的變數(shù)和進到另外一個 function 環(huán)境時可能會有不一樣的狀況。

因此在第一小節(jié)我們就下個小結(jié)論那就是每一段 JS 在運行的時候會根據(jù)片段程式碼所在的區(qū)塊有其特有的 環(huán)境

執(zhí)行環(huán)境的堆疊

對於執(zhí)行環(huán)境有了初步的概念之後我們還得知道 - 瀏覽器的 JS 直譯器通常是單執(zhí)行緒的,意味著一次只能夠做一件事。
也就是說當(dāng)一個事件被執(zhí)行的時候其他的任務(wù),事件等等就會被丟到執(zhí)行佇列中。這個東西我們就叫做執(zhí)行堆疊

我們已經(jīng)知道當(dāng) JS 開始跑的時候一開始會進入 global 執(zhí)行環(huán)境,如果您在 global 環(huán)境中呼叫了一個 function A (即: A();),這個時候就會建立新的 執(zhí)行環(huán)境 然後這個新的執(zhí)行環(huán)境會被放到執(zhí)行堆疊的最上面,同樣的如果你現(xiàn)在在 function A 裡面又叫了 function B 那麼就又會在建立一個執(zhí)行環(huán)境一樣放到執(zhí)行堆疊的最上方,瀏覽器永遠會先處理堆疊上最上面的執(zhí)行環(huán)境,一旦執(zhí)行環(huán)境裡面的任務(wù)都執(zhí)行完了那它就會被移掉換下一個

OK 這邊交代得有點亂,我們看到的程式碼的時候通常最小的執(zhí)行單位就是那一句一句的 statement 語句,一個語句交代了程式該做一件事。這些 statement 都會有自己的環(huán)境,也因此我們可以把環(huán)境在當(dāng)作一個上層單位。一個 context 裡面勢必存在一些任務(wù)(語句)。就把一個 context 想像成某個任務(wù)好了??纯聪旅娴墓?fàn)例可能比較有感覺

(function foo(i) {
  if (i === 3) {
    return;
  } else {
    foo(++i);
  }
}(0));

這段程式碼簡單的呼叫自己三次每一次把參數(shù)加一,每當(dāng) foo 被呼叫的時候新的 執(zhí)行環(huán)境 就被建立,然後當(dāng) 執(zhí)行環(huán)境 裡面的程式跑完的時候,就從堆疊中把 執(zhí)行環(huán)境 拿掉,把控制權(quán)交還給上一個環(huán)境一直到回到 global 為止。

關(guān)於執(zhí)行環(huán)境有 5 個重點要牢記在心

單執(zhí)行緒

同步執(zhí)行

只有一個 global context

function context 沒有限制

就算是自己呼叫自己只要 call function 就會建立執(zhí)行環(huán)境

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

所以我們現(xiàn)在知道了每一次 call function 的時候就會建立一個新的執(zhí)行環(huán)境,然而在 JS 直譯器內(nèi)部每次調(diào)用一個執(zhí)行環(huán)境都會有兩個階段

建立階段 當(dāng) function 被呼叫了但在開始執(zhí)行內(nèi)部程式碼之前

建立一個 scope chain 作用域鍊

建立變數(shù),function,和參數(shù)

設(shè)定 this 的值

執(zhí)行階段

賦值,設(shè)定 function 的參考和解譯執(zhí)行程式碼

概念上我們可以把一個 執(zhí)行環(huán)境 想像成一個物件,那麼這個物件大概會有三個屬性如下

executionContextObject = {
  scopeChain: { /* 變數(shù)物件 + 所有父代執(zhí)行環(huán)境物件的變數(shù)物件*/},
  variableObject: {/* 函式的參數(shù)/引數(shù),內(nèi)部的變數(shù)和函式*/ },
  this: {}
}

Variable Object 變數(shù)物件:根據(jù) ECMA-262 的說明,每一個執(zhí)行環(huán)境會有一個與相關(guān)連的變數(shù)物件,這個物件負責(zé)記錄執(zhí)行環(huán)境中定義的變數(shù)和函式。

Activation / Variable Object [AO/VO]

這一個執(zhí)行環(huán)境物件在 function 被調(diào)用的時候建立,不過在實際的 function 被執(zhí)行之前,這就是上面提到的階段 1 - 建立階段。在這個階段直譯器會建立 executionObject ,透過掃描函式傳入的參數(shù),內(nèi)部的函式宣告,變數(shù)宣告。結(jié)果會被記錄在executionObject變數(shù)物件 variableObject 中。

這裏我們大致模擬直譯器是如何執(zhí)行的流程

尋找呼叫 function 的程式碼

在執(zhí)行 function 之前建立 執(zhí)行環(huán)境

進入 建立階段

初始化 scope chain

建立 variable object

建立 arguments object 檢查執(zhí)行環(huán)境的參數(shù),初始化參數(shù)的名稱,值以及建立參考

掃描 function 的宣告

根據(jù)找到的每一個 function 在 variable object 建立,在這邊其實就是建立 function 名稱在記憶體中的參考指標

如果 function 名稱已經(jīng)存在那麼指標就會被覆寫

掃描執(zhí)行環(huán)境裡的變數(shù)

每一個變數(shù)的宣告都會被加入 variable object 的屬性中,並且初始化為 undefined,注意在這個階段並不會賦值

如果變數(shù)名稱存在就略過,繼續(xù)處理下一個變數(shù)

判斷決定 this 的值

執(zhí)行階段

執(zhí)行程式碼,賦值,一行一行跑

function foo(i) {
  var a = "hello";
  var b = function B() {

  };
  function c() {

  }
}
foo(22);

此時在建立階段我們就會得到如下的範(fàn)例

fooExecutionContext = {
  scopeChain: { ... },
  variableObject: {
    arguments: {
      0: 22,
      length: 1
    },
    i: 22,
    c: pointer to function c()
    a: undefined,
    b: undefined
  },
  this: { ... }
}

如您所見,在建立階段處理關(guān)於定義宣告的部分,此時並不會賦值,所以 function b 並沒有被參考。不過參數(shù)是唯一的例外,此時參數(shù)的值已經(jīng)被建立。一旦建立階段完成,剩下的流程就是開始執(zhí)行階段,當(dāng)執(zhí)行階段完成的時候執(zhí)行環(huán)境就會如下

fooExecutionContext = {
  scopeChain: { ... },
  variableObject: {
    arguments: {
      0: 22,
      length: 1
    },
    i: 22,
    c: pointer to function c()
    a: "hello",
    b: pointer to function B()
  },
  this: { ... }
}
變數(shù)宣告提升

您可以找到很多關(guān)於定義 Javascript hoisting 的資料,他們通常會解釋這就是一種把宣告提升到其所在區(qū)域內(nèi)頂端的行為,然而這樣並沒有解釋到細節(jié),為什麼會發(fā)生這件事,不過呢剛剛您已經(jīng)知道了關(guān)於整個直譯器解意的流程,現(xiàn)在您可以很清楚的明白為什麼會這樣了。

(function () {
  console.log("foo: " + typeof foo); // function pointer
  console.log("bar: " + typeof bar); // undefined

  var foo = "hello",
      bar = function() {
        return "world";
      };
  function foo() {
    return "hello";
  }
}());

現(xiàn)在我們可以回答關(guān)於上面這段程式碼的一些問題

為什麼我們在宣告之前可以存取 foo

如果我們看看 建立階段 的流程我們可以知道變數(shù)在這個時期早就被建立了

Foo 被宣告 2 次,為什麼 foo 是 function 而不是 undefined 或 string?

即使 foo 宣告了2次,我們知道在建立階段 function 會先被建立。因此變數(shù)已經(jīng)存在了在這個階段 string 不會被賦予 foo

因此在真正執(zhí)行 function 之前 foo 是會先被建立,等他真正跑完執(zhí)行階段的時候 foo 才會被覆寫成 "hello"

為什麼 bar 是 undefined ?

bar 就只是一個變數(shù),在這個階段並還沒賦值所以就是 undefined

總結(jié)

下個收斂的結(jié)論就是

每一個片段程式碼都會屬於某個執(zhí)行環(huán)境,或者說在開始執(zhí)行程式碼之前會先建立 執(zhí)行環(huán)境

執(zhí)行環(huán)境比喻來說就像是一個物件負責(zé)紀錄這個 環(huán)境 下相關(guān)的事物 變數(shù) function 等等

從上往下看這個執(zhí)行環(huán)境物件最重要的是 scope chain, variable object, this 這三個屬性

variable object 才是實際上記錄變數(shù),function,arguments 的地方

另外一個重要的點是 scope chain 他負責(zé)記錄每個環(huán)境之間切換的關(guān)聯(lián),例如從 global -> a()

每次開始建立執(zhí)行環(huán)境的時候就會分成兩個階段

開始建立執(zhí)行環(huán)境的時間點是在 function 被呼叫後,實際執(zhí)行內(nèi)部程式碼前

建立階段,初始化這個環(huán)境,除了 arguments 外其他都只是先定義變數(shù),函式指標,並沒有賦值

執(zhí)行階段,開始一行一行執(zhí)行,賦值
希望現(xiàn)在您可以更清楚關(guān)於 Javascript 如何運行您的程式碼,瞭解執(zhí)行環(huán)境,堆疊可以讓您更清楚您的程式碼在不同狀態(tài)下取到的值,如此一來相信您在組織 JS 的時候會有更好的寫法。

參考

0
1
2

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/78758.html

相關(guān)文章

  • [譯] 透過重新實作來學(xué)習(xí)參透閉包

    摘要:不過到底是怎麼保留的另外為什麼一個閉包可以一直使用區(qū)域變數(shù),即便這些變數(shù)在該內(nèi)已經(jīng)不存在了為了解開閉包的神秘面紗,我們將要假裝沒有閉包這東西而且也不能夠用嵌套來重新實作閉包。 原文出處: 連結(jié) 話說網(wǎng)路上有很多文章在探討閉包(Closures)時大多都是簡單的帶過。大多的都將閉包的定義濃縮成一句簡單的解釋,那就是一個閉包是一個函數(shù)能夠保留其建立時的執(zhí)行環(huán)境。不過到底是怎麼保留的? 另外...

    CoXie 評論0 收藏0
  • 手把手深入理解 webpack dev middleware 原理與相關(guān) plugins

    摘要:的架構(gòu)設(shè)計促使第三方開發(fā)者讓核心發(fā)揮出無限的潛力。當(dāng)然建置比起開發(fā)是較進階的議題,因為我們必須要理解內(nèi)部的一些事件。這個編譯結(jié)果包含的訊息包含模組的狀態(tài),編譯後的資源檔,發(fā)生異動的檔案,被觀察的相依套件等。 本文將對 webpack 周邊的 middleware 與 plugin 套件等作些介紹,若您對於 webpack 還不了解可以參考這篇彙整的翻譯。 webpack dev ser...

    gitmilk 評論0 收藏0
  • DevOps:持續(xù)整合 & 持續(xù)交付(Docker、CircleCI、AWS)

    摘要:原文出處持續(xù)整合持續(xù)交付這篇文章將一步一步介紹如何使用與來完成持續(xù)整合與持續(xù)交付的開發(fā)流程。前言什麼是持續(xù)整合持續(xù)交付持續(xù)整合持續(xù)交付,簡稱,具體介紹可以參考山姆鍋對持續(xù)整合持續(xù)部署持續(xù)交付的定義這篇文章。 原文出處:DevOps:持續(xù)整合&持續(xù)交付(Docker、CircleCI、AWS) showImg(https://segmentfault.com/img/bVlxh...

    Euphoria 評論0 收藏0
  • [譯 + 補充] Webpack 2 入門

    摘要:目錄許多開發(fā)者會把的目錄命名為但這並不強迫。所有的檔案都會使用從被編譯成。同時有個小小的重點那就是我們可已觀察編譯後的檔案大小。在專案目錄下執(zhí)行可以觀察截至目前為止的結(jié)果。我們的目標是要把編譯封裝到我們的中。 在今時今日,webpack 已經(jīng)成為前端開發(fā)非常重要的工具之一。本質(zhì)上它是一個 Javascript 模組封裝工具,但透過 loaders 和 plugins 它也可以轉(zhuǎn)換封裝其...

    betacat 評論0 收藏0
  • webpack配置信息說明

    摘要:參考令人困惑的地方項目名稱項目名稱版本描述作者開源協(xié)議主文件指定了運行腳本命令的命令行縮寫,比如這是的指定了運行時,所要執(zhí)行的命令。要解析并且完成相應(yīng)的功能,這些基本都是必須的。 參考 Webpack——令人困惑的地方 package.json { name: 項目名稱, //項目名稱 version: 1.0.0, //版本 description: vue+...

    wind3110991 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<