這篇文章帶給大家的內容是關於js中對執行上下文以及變數物件的解析 ,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。
執行上下文(Execution Context)
JavaScript程式碼執行的過程,包括編譯和執行兩個階段,編譯就是透過詞法分析,建構抽象抽象語法樹,並且編譯成機器識別的指令,在JavaScript程式碼編譯階段,作用域規則就已經確定了;在程式碼執行階段,或者函數一旦調用,便會創建執行上下文(Execution Context),也叫執行環境
在ECMA-262中有下列一段定義
當控制器轉入ECMA 腳本的可執行程式碼時,控制器會進入一個執行環境。目前活動的多個執行環境在邏輯上形成一個堆疊結構。此邏輯棧的最頂層的執行環境稱為目前運行的執行環境。任何時候,當控制器從目前執行的執行環境相關的可執行程式碼轉入與該執行環境無關的可執行程式碼時,會建立一個新的執行環境。新建的這個執行環境會被推入堆疊中,成為目前運行的執行環境.
這也是一個抽象的概念,在一段JavaScript程式碼中,會建立多個執行上下文,執行上下文定義了變數或函數有權存取的其他資料, ,透過閱讀規範及相關文檔,了解到執行上下文(簡稱EC)主要包括三個點,用偽代碼表示如下:
EC = { this: // 绑定this指向为当前执行上下文, 如果函数属于全局函数,则this指向window scopeChain: [] // 创建当前执行环境的作用域链, VO: {} // 当前环境的变量对象(Variable Object),每个环境都有一个与之关联的变量对象 }
看下面這段程式碼:
var a = 1; function foo() { var b = 2; function bar() { console.log(b) } bar() console.log(a); } foo()
1.執行這段程式碼,首先會建立全域上下文globleEC,並推入執行上下文堆疊中;
- ##2.當調用foo()時便會建立foo的上下文fooEC,並推入執行上下文堆疊中;
- #3.當呼叫bar()時便會建立bar的上下文barEC,並推入執行上下文堆疊中;
- 4.當bar函數執行完,barEC就會從執行上下文堆疊中彈出;
- 5.當foo函數執行完,fooEC便會從執行上下文堆疊中彈出;
- #6.在瀏覽器視窗關閉後,全域上下文globleEC會從執行上下文堆疊中彈出;
變數物件(Variable Object):
每一個執行環境都有一個與之關聯的變數對象,是一個抽象的概念,環境中定義的所有變數和函數都保存在這個物件中。雖然我們寫的程式碼無法存取這個對象,但單解析器在處理資料時會在後台使用它們。 當瀏覽器第一次載入js腳本程式的時候, 預設進入全域執行環境, 這次的全域環境變數物件為window, 在程式碼中可以存取。 如果環境是函數, 則將此活動物件做為當前上下文的變數物件(VO = AO), 此時變數物件是不可透過程式碼來存取的,下面主要對活動物件進行講解。活動物件(Activation Object)
1、初始化活動物件(下文縮寫為AO)當函數一調用,立刻建立當前上下文的活動對象, 並將活動對像作為變數對象,透過arguments屬性初始化,值為arguments對象(傳入的實參集合,與形參無關,形參做為局部環境的局部變數被定義)
AO = { arguments: <argo> };</argo>arguments物件有以下屬性:
length: 真正傳遞參數的數量;
callee: 指向目前函數的參考,也就是被呼叫的函數;
#'類別index': 字串型別的整數, 值就是arguments物件中物件下標的值,arguments物件應和陣列加以區別, 它就是arguments物件,只是能和陣列具有相同的length屬性,和可以透過下標來存取值
#
function show (a, b, c) { // 通过Object.prototype.toString.call()精准判断类型, 证明arguments不同于数组类型 var arr = [1, 2, 3]; console.log(Object.prototype.toString.call(arr)); // [object Array] console.log(Object.prototype.toString.call(arguments)); // [object Arguments] console.log(arguments.length) // 2 传递进来实参的个数 console.log(arguments.callee === show) // true 就是被调用的函数show自身 //参数共享 console.log(a === arguments[0]) // true a = 15; console.log(arguments[0]) // 15 arguments[0] = 25; console.log(a) // 25; 但是,对于没有传进来的参数c, 和arguments的第三个索引是不共享的 c = 25; console.log(arguments[2]) // undefined argument[2] = 35; console.log(c) // 25 } show(10, 20);接著往下走,這才是關鍵的地方,執行環境的程式碼被分成兩個階段來處理:
- #進入執行環境
- 執行函數的程式碼
在進入執行環境這個階段:
所有形參宣告:
形參牌名稱作為活動物件屬性被創建, 如果傳遞實參, 值就為實參值, 如果沒有傳遞參數, 值就為undefined#所有函數聲明:
函數名稱作為活動對象的屬性被創建,值是一個指標在記憶體中, 指向這個函數,如果變數物件已經存在相同名稱的屬性, 則完全替換。所有變數宣告:
所有变量名称作为活动对象的属性被创建, 值为undefined,但是和函数声明不同的是, 如果变量名称跟已经存在的属性(形式参数和函数)相同、则不会覆盖
function foo(a, b) { var c = 10; function d() { console.log('d'); } var e = function () { console.log('e'); }; (function f() {}) if (true) { var g = 20; } else { var h = 30; } } foo(10);
此时在进入foo函数执行上下文时,foo的活动对象fooAO为:
fooAO = { arguments: { 0: 10, length: 1 }, a: 10, b: undefined, c: fundefined, d: <d> //指向d函数的指针, e: undefined, g: undefined, h: undefined // 虽然else中的代码永远不会执行,但是h仍然是活动对象中的属性 }</d>
这个例子做如下几点说明:
1.关于函数,只会创建函数声明作为活动对象的属性, 而f函数作为函数表达式并不会出现在活动对象(AO)中
2.e虽然值是一个函数, 但是作为变量属性被活动对象创建
3、代码执行阶段
在进入执行上下文阶段,活动对象拥有了属性,但是很多属性值为undefined, 到代码执行阶段就开始为这些属性赋值了
还是上面的代码例子, 此时活动对象如下:
fooAO = { arguments: { 0: 10, length: 1 }, a: 10, b: undefined, c: 10, // 赋值为undefined d: <d> //指向d函数的指针, e: <d> // 指向e函数的指针 g: 20, h: undefined // 声明h变量,但是没有赋值 }</d></d>
变量对象包括:{ arguments对象+函数形参+内部变量+函数声明(但不包含表达式) }
这时这个活动对象, 即作为当前执行环境的变量对象会被推到此执行环境作用域链的最前端(作用域链本篇不做介绍,会在下一篇文章中单独讲解作用域和作用域链), 假定执行环境为一个对象,则整个执行环境可以访问到的属性如下:
伪代码如下:
fooExecutionContext = { scopeChain: [], //fooAO +所有父执行环境的活动对象, fooAO: { arguments: { 0: 10, length: 1 }, a: 10, b: undefined, c: 10, // 赋值为undefined d: <d> //指向d函数的指针, e: <d> // 指向e函数的指针 g: 20, h: undefined }, this: 当前执行环境的上下文指针 }</d></d>
补充:
下面的例子为了说明一下变量声明的顺序及变量同名不会影响函数声明
console.log(foo); // foo的函数体 var foo = 10; console.log(foo) // 10 function foo() {}; foo = 20; console.log(foo); // 20
在代码执行之前, 就会读取函数声明,变量声明的顺序在函数声明和形参声明之后, 整个流程如下:
进入执行环境阶段:
1. var VO = {} 2. VO[foo] = 'foo函数指针' 3. 扫描到var foo = 10, // 但是foo做为function已经声明,所以变量声明不会影响同名的函数声明,如果代码中没有foo函数声明的话,则foo为undefined
代码执行阶段:
1. VO[foo] = 10; 2. VO[foo] = 20;
解析代码完成。
相关推荐:
以上是js中對執行上下文以及變數物件的解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!