首頁 web前端 js教程 javascript從定義到執行,那些你不知道的事

javascript從定義到執行,那些你不知道的事

May 16, 2016 pm 03:21 PM
javascript 定義 執行

javascript從定義到執行,JS引擎在實作層做了很多初始化工作,因此在學習JS引擎工作機制之前,我們需要引入幾個相關的概念:執行環境堆疊、全域物件、執行環境、變數對象、活動物件、作用域和作用域鍊等,這些概念正是JS引擎工作的核心元件。

這篇文章的目的不是孤立的為你講解每一個概念,而是透過一個簡單的demo來展開分析,全局講解JS引擎從定義到執行的每一個細節,以及這些概念在其中所扮演的角色。

var x = 1; //定义一个全局变量 x
function A(y){
  var x = 2; //定义一个局部变量 x
  function B(z){ //定义一个内部函数 B
    console.log(x+y+z);
  }
  return B; //返回函数B的引用
}
var C = A(1); //执行A,返回B
C(1); //执行函数B
登入後複製

這個demo是一個閉包,執行結果是4,下面我們將分全域初始化、執行函數A、執行函數B 三個階段來分析JS引擎的工作機制:

一、全域初始化

JS引擎在進入一段可執行的程式碼時,需要完成以下三個初始化工作:

首先,建立一個全域物件(Global Object) , 這個物件全域只存在一份,它的屬性在任何地方都可以訪問,它的存在伴隨著應用程式的整個生命週期。全域物件在建立時,將Math,String,Date,document 等常用的JS物件作為其屬性。由於這個全域物件不能透過名字直接訪問,因此還有另外一個屬性window,並將window指向了自身,這樣就可以透過window訪問這個全域物件了。用偽代碼模擬全域物件的大體結構如下:

//创建一个全局对象
var globalObject = { 
  Math:{},
  String:{},
  Date:{},
  document:{}, //DOM操作
  ...
  window:this //让window属性指向了自身
}
登入後複製

然後,JS引擎需要建構一個執行環境棧( Execution Context Stack) ,同時,也要建立一個全域執行環境(Execution Context)EC ,並將這個全域執行環境EC壓入執行環境堆疊中。執行環境棧的作用是為了確保程式能夠按照正確的順序被執行。在javascript中,每個函數都有自己的執行環境,當執行一個函數時,函數的執行環境就會被推入執行環境堆疊的頂部並取得執行權。當這個函數執行完畢,它的執行環境又從這個堆疊的頂端被刪除,並且把執行權並還給之前執行環境。我們用偽代碼來模擬執行環境棧和EC的關係:

var ECStack = []; //定义一个执行环境栈,类似于数组

var EC = {};  //创建一个执行空间,
//ECMA-262规范并没有对EC的数据结构做明确的定义,你可以理解为在内存中分配的一块空间

ECStack.push(EC); //进入函数,压入执行环境
ECStack.pop(EC); //函数返回后,删除执行环境
登入後複製

最後,JS引擎也要建立一個與EC關聯的全域變數物件(Varibale Object ) VO,  並把VO指向全域對象,VO中不僅包含了全域對象的原有屬性,還包含在全域定義的變數x 和函數A,同時,在定義函數A的時候,也為A 增加了一個內部屬性scope,並將scope指向了VO。每個函數在定義的時候,都會建立一個與之關聯的scope屬性,scope總是指向定義函數時所在的環境。此時的ECStack結構如下:

ECStack = [  //执行环境栈
  EC(G) = {  //全局执行环境
    VO(G):{ //定义全局变量对象
      ... //包含全局对象原有的属性
      x = 1; //定义变量x
      A = function(){...}; //定义函数A
      A[[scope]] = this; //定义A的scope,并赋值为VO本身
    }
  }
];
登入後複製

二、執行函數A

執行進入A(1) 時,JS引擎需要完成以下工作:

首先,JS引擎會建立函數A的執行環境EC,然後EC推入執行環境堆疊的頂部並取得執行權。此時執行環境堆疊中有兩個執行環境,分別是全域執行環境和函數A執行環境,A的執行環境在堆疊頂,全域執行環境在堆疊的底部。然後,創建函數A的作用域鏈(Scope Chain) ,在javascript中,每個執行環境都有自己的作用域鏈,用於標識符解析,當執行環境被創建時,它的作用域鏈就會初始化為目前運行函數的scope所包含的物件。

接著,JS引擎會創建一個當前函數的活動對象(Activation Object) AO,這裡的活動對象扮演著變數對象的角色,只是在函數中的叫法不同而已(你可以認為變數對象是一個總的概念,而活動物件是它的一個分支), AO中包含了函數的形參、arguments物件、this物件、以及局部變數和內部函數的定義,然後AO會被推入作用域鏈的頂端。要注意的是,在定義函數B的時候,JS引擎同樣也會為B添加了一個scope屬性,並將scope指向了定義函數B時所在的環境,定義函數B的環境就是A的活動對象AO,而AO位於鍊錶的前端,由於鍊錶具有首尾相連的特點,因此函數B的scope指向了A的整個作用域鏈。 我們再來看看此時的ECStack結構:

ECStack = [  //执行环境栈
  EC(A) = {  //A的执行环境
    [scope]:VO(G), //VO是全局变量对象
    AO(A) : { //创建函数A的活动对象
      y:1,
      x:2, //定义局部变量x
      B:function(){...}, //定义函数B
      B[[scope]] = this; //this指代AO本身,而AO位于scopeChain的顶端,因此B[[scope]]指向整个作用域链
      arguments:[],//平时我们在函数中访问的arguments就是AO中的arguments
      this:window //函数中的this指向调用者window对象
    },
    scopeChain://链表初始化为A[[scope]],然后再把AO加入该作用域链的顶端,此时A的作用域链:AO(A)->VO(G)
  },
  EC(G) = {  //全局执行环境
    VO(G):{ //创建全局变量对象
      ... //包含全局对象原有的属性
      x = 1; //定义变量x
      A = function(){...}; //定义函数A
      A[[scope]] = this; //定义A的scope,A[[scope]] == VO(G)
    }
  }
];
登入後複製

三、 执行函数B

函数A被执行以后,返回了B的引用,并赋值给了变量C,执行 C(1) 就相当于执行B(1),JS引擎需要完成以下工作:

首先,还和上面一样,创建函数B的执行环境EC,然后EC推入执行环境栈的顶部并获取执行权。 此时执行环境栈中有两个执行环境,分别是全局执行环境和函数B的执行环境,B的执行环境在栈顶,全局执行环境在栈的底部。(注意:当函数A返回后,A的执行环境就会从栈中被删除,只留下全局执行环境)然后,创建函数B的作用域链,并初始化为函数B的scope所包含的对象,即包含了A的作用域链。最后,创建函数B的活动对象AO,并将B的形参z, arguments对象 和 this对象作为AO的属性。此时ECStack将会变成这样:

ECStack = [  //执行环境栈
  EC(B) = {  //创建B的执行环境,并处于作用域链的顶端
    [scope]:AO(A), //指向函数A的作用域链,AO(A)->VO(G)
    var AO(B) = { //创建函数B的活动对象
      z:1,
      arguments:[],
      this:window
    }
    scopeChain://链表初始化为B[[scope]],再将AO(B)加入链表表头,此时B的作用域链:AO(B)->AO(A)-VO(G)
  },
  EC(A), //A的执行环境已经从栈顶被删除,
  EC(G) = {  //全局执行环境
    VO:{ //定义全局变量对象
      ... //包含全局对象原有的属性
      x = 1; //定义变量x
      A = function(){...}; //定义函数A
      A[[scope]] = this; //定义A的scope,A[[scope]] == VO(G)
    }
  }
];
登入後複製

当函数B执行“x+y+z”时,需要对x、y、z 三个标识符进行一一解析,解析过程遵守变量查找规则:先查找自己的活动对象中是否存在该属性,如果存在,则停止查找并返回;如果不存在,继续沿着其作用域链从顶端依次查找,直到找到为止,如果整个作用域链上都未找到该变量,则返回“undefined”。从上面的分析可以看出函数B的作用域链是这样的:

AO(B)->AO(A)->VO(G)
因此,变量x会在AO(A)中被找到,而不会查找VO(G)中的x,变量y也会在AO(A)中被找到,变量z 在自身的AO(B)中就找到了。所以执行结果:2+1+1=4.

简单的总结语

了解了JS引擎的工作机制之后,我们不能只停留在理解概念的层面,而要将其作为基础工具,用以优化和改善我们在实际工作中的代码,提高执行效率,产生实际价值才是我们的真正目的。就拿变量查找机制来说,如果你的代码嵌套很深,每引用一次全局变量,JS引擎就要查找整个作用域链,比如处于作用域链的最底端window和document对象就存在这个问题,因此我们围绕这个问题可以做很多性能优化的工作,当然还有其他方面的优化,此处不再赘述,本文仅当作抛砖引玉吧!

以上就是本文的全部内容,希望对大家的学习有所帮助。

【相关教程推荐】

1. JavaScript视频教程
2. JavaScript在线手册
3. bootstrap教程

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

MySQL 複合主鍵的定義與作用 MySQL 複合主鍵的定義與作用 Mar 15, 2024 pm 05:18 PM

MySQL中的複合主鍵是指表中由多個欄位組合而成的主鍵,用來唯一標識每筆記錄。與單一主鍵不同的是,複合主鍵由多個欄位的值組合在一起形成。在建立表格的時候,可以透過指定多個欄位為主鍵來定義複合主鍵。為了示範複合主鍵的定義與作用,我們先建立一個名為users的表,其中包含了id、username和email這三個字段,其中id是自增主鍵,user

什麼是Discuz? Discuz的定義與功能介紹 什麼是Discuz? Discuz的定義與功能介紹 Mar 03, 2024 am 10:33 AM

《探索Discuz:定義、功能及程式碼範例》隨著網路的快速發展,社群論壇已成為人們獲取資訊、交流觀點的重要平台。在眾多的社群論壇系統中,Discuz作為國內較知名的一種開源論壇軟體,備受廣大網站開發者和管理員的青睞。那麼,什麼是Discuz?它又有哪些功能,能為我們的網站提供怎樣的幫助呢?本文將對Discuz進行詳細介紹,並附上具體的程式碼範例,幫助讀者更

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

PHP介面簡介及其定義方式 PHP介面簡介及其定義方式 Mar 23, 2024 am 09:00 AM

PHP介面簡介及其定義方式PHP是一種廣泛應用於Web開發的開源腳本語言,具有靈活、簡單、強大等特性。在PHP中,介面(interface)是一種定義多個類別之間公共方法的工具,實現了多態性,讓程式碼更加靈活和可重複使用。本文將介紹PHP介面的概念及其定義方式,同時提供具體的程式碼範例展示其用法。 1.PHP介面概念介面在物件導向程式設計中扮演著重要的角色,定義了類別應

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

全角字符的定義及使用 全角字符的定義及使用 Mar 25, 2024 pm 03:33 PM

什麼是全角字元?在電腦編碼系統中,全角字元是一種佔用兩個標準字元位置的字元編碼方式。相對應的,佔用一個標準字元位置的字元編碼方式稱為半角字元。全角字元通常用於中文、日文、韓文等亞洲文字的輸入、顯示和列印。在中文輸入法和文字編輯中,全角字符與半角字符的使用場景是有所區別的。全角字符的使用中文輸入法:在中文輸入法中,通常全角字符用於輸入中文字符,例如漢字、標

See all articles