this, eval('this'), (0, eval)('this'))

來源:互聯網
上載者:User

標籤:style   blog   java   使用   io   strong   ar   art   

今天又被一段javascript代碼神奇到了

function test() {     console.log(this, eval(‘this‘), (0, eval)(‘this‘)); };var a = {};test.apply(a);

在現代瀏覽器的console裡跑上邊的代碼,結果應該類似是下邊這樣

Object {} Object {} Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}

this和eval(‘this‘)的結果是Object {},也就是a。而(0, eval)(‘this‘)的結果就變成了window,也就是全域環境裡的this。

 

為什麼呢? 

 

解釋這段代碼其實需要梳理兩個概念

1. this是什麼
2. eval的scope

 

Javascript方程的四種調用模式

首先簡單介紹下這部分知識,我是從JavaScript: The Good Parts裡讀的,這書真是看過的都說好。

JavaScript裡調用函數時除了函式宣告裡的參數外還會外加一個參數this,而this的值取決於調用函數的方式。

四種調用方式分別是:

1. 方法調用(Method Invocation Pattern):函數為一個對象的屬性,比如myObj.functionName(param),this為該對象(myObj)

2. 函數調用(Function Invocation Pattern):像functionName(param)這樣的調用模式,this為全域對象

3. 建構函式調用(Constructor Invoation Pattern): 比較特殊的一種,就是new ObjConstructor(param)這類的,this為建立的那個新對象

4. apply調用(Apply Invocation Pattern):也就是我們一開始的代碼的test.apply(a)裡這種,this為apply的參數數組裡的第一個,在這裡也就是a

弄懂了這些,我們知道test.apply(a)調用的test裡,this應該就是a。但是為什麼最後一個(0, eval)(‘this‘)輸出的不是a呢?

 

ECMAScript 5裡對eval的規範

首先引用幾條百度來的ECMAScript5.1規範中文版

10.4.2 進入eval代碼 

當控制流程進入 eval 代碼 的執行環境時,執行以下步驟:

  1. 如果沒有調用環境,或者 eval 代碼 並非通過直接調用(15.1.2.1.1)eval 函數進行評估的,則
    1. 按(10.4.1.1)描述的初始化全域執行環境的方案,以 eval 代碼 作為 C 來初始化執行環境。
  2. 否則
    1. 將 this 綁定設定為當前執行環境下的 this 綁定。
    2. 將詞法環境設定為當前執行環境下的 詞法環境 。
    3. 將變數環境設定為當前執行環境下的變數環境。
  3. 如果 eval 代碼 是 strict 模式下的代碼 ,則
    1. 令 strictVarEnv 為以詞法環境為參數調用 NewDeclarativeEnvironment 得到的結果。
    2. 設定詞法環境為 strictVarEnv。
    3. 設定變數環境為 strictVarEnv。
  4. 按 10.5 描述的方案,使用 eval 代碼 執行定義綁定初始化步驟。
15.1.2.1.1 直接調用eval

 一個 eval 函數的直接調用是表示為符合以下兩個條件的 CallExpression:

 解釋執行 CallExpression 中的 MemberExpression 的結果是個 引用 ,這個引用擁有一個 環境記錄項 作為其基值,並且這個引用的名稱是 "eval"。

 以這個 引用 作為參數調用 GetValue 抽象操作的結果是 15.1.2.1 定義的標準內建函數。

10.4.1.1 初始化全域執行環境

 以下步驟描述 ECMA 指令碼的全域執行環境 C 的建立過程:

  1. 將變數環境設定為 全域環境 。
  2. 將詞法環境設定為 全域環境 。
  3. 將 this 綁定設定為 全域對象 。

 

不考慮strict 模式的情況,那麼簡單說,就是ES5裡執行eval分直接調用和非直接調用兩種情況。

在直接調用的情況下,eval的執行環境就是調用它的caller的執行環境。

在非直接調用的情況下,eval的執行環境則是全域環境。

 

判定是否直接調用eval的依據對代碼的文法解析。

比如最簡單的,eval(‘this‘)就是一個CallExpression,裡邊eval是MemberExpression。按照直接調用的要求,MemberExpression的結果必須是個名稱為eval的引用,我們這裡的eval完全符合要求。所以eval(‘this‘)為直接調用。

再比如(0, eval)(‘this‘)的MemberExpression是(0, eval),JS裡逗號操作符會調用內建的GetValue,所以(0, eval)的結果並不是引用,不符合直接調用的要求,所以(0, eval)(‘this‘)是一個非直接調用。

相對的,(eval)並沒有逗號操作符調用GetValue,所以它的結果還是一個引用,所以(eval)(‘this‘)是直接調用。這也就解釋了一開始的代碼的輸出結果。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.