深入學習JS執行--創建執行上下文(變數對象,作用域鏈,this)

来源:http://www.cnblogs.com/Ry-yuan/archive/2017/11/23/7868029.html
-Advertisement-
Play Games

一、介紹 本篇繼上一篇 "深入理解js執行 單線程的JS" ,這次我們來深入瞭解js執行過程中的執行上下文。 本篇涉及到的名詞:預執行,執行上下文,變數對象,活動對象,作用域鏈,this等 二、預執行 在上一篇說到,在js代碼被執行,執行上下文會被壓進執行棧中,但是在此之前還有一步工作要做,就是創建 ...


一、介紹

本篇繼上一篇深入理解js執行--單線程的JS,這次我們來深入瞭解js執行過程中的執行上下文。

本篇涉及到的名詞:預執行,執行上下文,變數對象,活動對象,作用域鏈,this等

二、預執行

在上一篇說到,在js代碼被執行,執行上下文會被壓進執行棧中,但是在此之前還有一步工作要做,就是創建好執行上下文,因為創建好才能被壓進去啊。

創建執行上下文就是預執行過程: 接下來說說創建執行上下文的細節部分。

三、創建執行上下文

(1)執行上下文組成

執行上下文:也叫一個執行環境,有全局執行環境和函數執行環境兩種。每個執行環境中包含這三部分:變數對象/活動對象作用域鏈this的值

代碼模擬

//可以把執行上下文看作一個對象
exeContext = {
    VO = [...],  //VO代表變數對象,保存變數和函數聲明
    scopeChain = [...];  //作用域鏈
    thisValue = {...};  //this的值
}

創建執行上下文就是創建變數對象,作用域鏈和this過程

接下來就分別細說創建變數對象/活動對象,作用域鏈,this值的過程。

(2)變數對象(variable object)

變數對象中存儲了在上下文(環境)中定義的變數和函數聲明

創建變數對象(VO)時就是將各種變數和函數聲明進行提升的環節:

//用下麵代碼為例子
console.log(a);
console.log(b);
console.log(c);
console.log(d);
var a = 100;
b = 10;
function c(){};
var d = function(){};

上述代碼的變數對象:

//這裡用VO表示變數對象
VO = {
    a = undefined; //有a,a使用var聲明,值會被賦值為undefined
    //沒有b,因為b沒用var聲明
    c = function c (){}  //有c,c是函數聲明,並且c指向該函數
    d = undefined; //有d,d用var聲明,值會被賦值為undefined
}

解說:執行上述代碼的時候,會創建一個全局執行上下文,上下文中包含上面變數對象,創建完執行上下文後,這個執行上下文才會被壓進執行棧中。開始執行後,因為js代碼一步一步被執行,後面賦值的代碼還沒被執行到,所以使用console.log函數列印各個變數的值是變數對象中的值。

在運行到第二行時會報錯(報錯後就不再執行了),因為沒有b(b is no defined)。把第二行註釋掉後,再執行各個結果就是VO裡面的對應的值。

講到這裡我想大家對變數對象理解了吧,以及對變數提升和函數提升有個深入瞭解。

(3)活動對象(activation object)

活動對象是在函數執行上下文裡面的,其實也是變數對象,只是它需要在函數被調用時才被激活,而且初始化arguments,激活後就是看做變數對象執行上面一樣的步驟。

//例子
function fn(name){
    var age = 3;
    console.log(name);
}
fn('ry');

當上面的函數fn被調用,就會創建一個執行上下文,同時活動對象被激活

//活動對象
AO = {
    arguments : {0:'ry'},  //arguments的值初始化為傳入的參數
    name : ry,  //形參初始化為傳進來的值
    age : undefined  //var 聲明的age,賦值為undefined
}

活動對象其實也是變數對象,做著同樣的工作。其實不管變數還是活動對象,這裡都表明瞭,全局執行和函數執行時都有一個變數對象來儲存著該上下文(環境內)定義的變數和函數。

(4)作用域鏈(scope chain)

在創建執行上下文時還要創建一個重要的東西,就是作用域鏈。每個執行環境的作用域鏈由當前環境的變數對象及父級環境的作用域鏈構成。

創建作用域鏈過程:

//以本段代碼為例
function fn(a,b){
    var x = 'string',
}
fn(1,2);

1.函數被調用前,初始化function fn,fn有個私有屬性[[scope]],它會被初始化為當前全局的作用域,fn.[[scope]="globalScope"。

2.調用函數fn(1,2),開始創建fn執行上下文,同時創建作用域鏈fn.scopeChain = [fn.[[scope]]],此時作用域鏈中有全局作用域。

3.fn活動對象AO被初始化後,把活動對象作為變數對象推到作用域鏈前端,此時fn.scopeChain = [fn.AO,fn.[[scope]]],構建完成,此時作用域鏈中有兩個值,一個當前活動對象,一個全局作用域。

fn的作用域鏈構建完成,作用域鏈中有兩個值,第一個是fn函數自身的活動對象,能訪問自身的變數,還有一個是全局作用域,所以fn能訪問外部的變數。這裡就說明瞭為什麼函數中能夠訪問函數外部的變數,因為有作用域鏈,在自身找不到就順著作用域鏈往上找。

(5)this的值

上面說過執行上下文有兩種,一個全局執行上下文,一個函數執行上下,下麵分別說說這兩種上下文的this。

a.全局執行上下文的this

指向window全局對象

b.函數執行上下文的this(主要講函數的this)

在《JavaScript權威指南》中有這麼幾句話:
1.this是關鍵字,不是變數,不是屬性名,js語法不允許給this賦值。
2.關鍵字this沒有作用域限制,嵌套的函數不會從調用它的函數中繼承this。
3.如果嵌套函數作為方法調用,其this指向調用它的對象。
4.如果嵌套函數作為函數調用,其this值是window(非嚴格模式),或undefined(嚴格模式下)。

解讀一下: 上面說的概括了this兩種值的情況:
1.函數直接作為某對象的方法被調用則函數的this指向該對象。
2.函數作為函數直接獨立調用(不是某對象的方法),或是函數中的函數,其this指向window。

我們看幾個慄子便可理解:
慄子1:(這個例子我相信都能理解)當函數被獨立運行時,其this的值指向window對象。

function a(){
    console.log(this);
}
//獨立運行
a();  //window

慄子2:(函數中函數,這裡嵌套了個外圍函數)這裡也是指向window對象,也相當於函數作為函數調用,就是獨立運行。其實這個例子也說明閉包的this指向Window。

//外圍函數
function a(){
    //b函數在裡面
    function b(){
        console.log(this);
    }
    //雖然在函數中,但b函數獨立運行,不是那個對象的方法
    b();
}
a();  //window

慄子3:(再寫複雜點的話)x函數即使在對象裡面,但它是函數中的函數,也是作為函數運行,不是Object的方法。getName才是objcet的方法,所以getName的this指向object(在下個慄子有)。

//一個對象
var object = {
    //getName是Object的方法
    getName : function(){
        //x是getName裡面的函數,它是作為函數調用的,this就是window啦
        function x(){
            console.log(this);
        }
        x();
    }
}
object.getName();  //window

以上三個都是輸出window,下麵是this指向某個對象的情況。

慄子4:函數作為某個對象的方法被調用。

//一個對象
var object = {
    name : "object",
    //getName是Object的方法
    getName : function(){
        console.log(this === object);
    }
}
object.getName(); //true , 說明this指向了object

這裡的getName中的this是指向objct對象的,因為getName是object的一個方法,它作為對象方法被調用。

慄子5:再來個慄子。

var name = "window";
var obj = {
    name : "obj"
};
function fn (){
    console.log(this.name);
}

//將fn通過call或bind或apply直接綁定給obj,從而成為obj的方法。
fn.call(obj);  //obj

再總結一下this的值

全局執行上下文:this的值是window
函數執行上下文:this的值兩種:
1.函數中this指向某對象,因為函數作為對象的方法:怎麼看函數是對象的方法,一種是直接寫在對象裡面(不是嵌套在對象方法中的函數,不懂再看看慄子3),另一種是通過call等方法直接綁定在對象中。

2.函數中this指向window:函數獨立運行,不是對象的方法,函數中的函數(閉包),其this指向window。

四、總結整個js代碼執行過程

(1)JS執行過程

js代碼執行分成了兩部分:預執行和執行

  1. 預執行:創建好執行上下文,有兩種,一種是開始執行js代碼就創建全局的執行上下文,一種是當某個函數被調用時創建它自己的函數執行上下文。這裡也就是本節主要講的東西,創建執行上下文的三個重要成分。
  2. 執行:在執行棧中執行,棧頂的執行上下文獲得執行權,並按順序執行當前上下文中的代碼,執行完後彈棧銷毀上下文,執行權交給下一個棧頂執行上下文。

(2)放上圖示

某個執行上下文生命周期:

五、後話

整個js的執行過程就這樣了,一開始可能有點難理解,但看多幾遍就慢慢領會了。希望大家能夠理解。如果覺得寫得好,記得點贊,關註哦。

本文出自博客園:http://www.cnblogs.com/Ry-yuan/
作者:Ry(淵源遠願)
歡迎轉載,轉載請標明出處,保留該欄位。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在學習Java編程完之後,學員們面臨的就是就業問題。作為一名Java開發工程師,企業在招聘的時候,也是有一定的標準的。 為了幫助大家更好的找到適合自己的工作,在這裡分享了作為一名Java開發工程師需要掌握的專業技能,大家可以參考一下。 一、熟練的使用Java語言進行面向對象程式設計,有良好的編程習慣 ...
  • 在學習廖雪峰前輩的JavaScript教程中,遇到了一些需要註意的點,因此作為學習筆記列出來,提醒自己註意! 如果大家有需要,歡迎訪問前輩的博客https://www.liaoxuefeng.com/學習。 函數定義 在JavaScript中,函數的定義如下: 上面abs()函數的定義如下: fun ...
  • 初始化時分支,也稱載入時分支是一種優化模式,當知道某個條件在整個程式生命周期內不會發生改變的時候,進隊該條件測試一次是很有意義的。 以事件監聽代碼為例: 以上代碼的問題在於效率比較低下,每次在調用addListener()或removeListener()時,都會重覆地執行相同的檢查 當使用初始化時 ...
  • input:focus{color:blue}//游標顏色 input{ text-shadow: 0px 0px 0px red;//文字顏色 -webkit-text-fill-color: transparent; } ...
  • 學習重點: 1、熟悉HTML4定義的格式化文本標簽; 2、掌握HTML5新增的文本標簽; 3、正確選用標簽設計網頁文本信息。 4.1 使用文本標簽 4.1.1 標題文本 <h1>~<h6>用於定義標題。 <h1>定義最大的標題,表示最重要的信息;<h6>定義最小的標題,表示最次要的信息。 <h1>只 ...
  • 最近工作中需要在前端頁面中使用代碼完成剪貼板的讀寫,網上搜索了下相應的資料,記錄下... 這個功能有兩個辦法一個是js方式,一個是使用flash 一、JS方法 1、複製 首先複製的過程分為兩步曲,無論是使用手工還是代碼,先來看看手工的 a、使用游標選中內容 b、通過ctrl + c 進行複製 其實在 ...
  • 上篇(Angular2快速入門-2.創建一個新聞列表)已經完成新聞列表的展示,並且點擊新聞列表的時候,下麵可以展示出新聞的詳細信息,這節我們把新聞詳細和新聞列表頁面分離出來 新聞詳細單獨一個component 第一、創建news-detail.component 1)創建news-detail.co ...
  • 前段時間,終於仔仔細細的把pt、px、em、rem瞭解了一遍,簡單整理了一下做個記錄。 pt、px、em、rem都是什麼 pt單位名稱為點(Point),絕對長度單位。現在網頁中出現得很少甚至不出現,常用於印刷行業。 單位換算:1in = 2.54cm = 25.4 mm = 101.6q = 72 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...