標籤:

求解一小段代碼,完全不理解eval的作用機制?


因為ES規範里限定了eval的兩種使用模式。

有以下兩種模式

1. Direct call

2. Indirect call

如果eval在CallMemberExpression 中出現時是作為一個Reference出現的,那麼解釋器把代碼作為Direct call處理,否則按照Indirect call處理。

function foo(){
var a=2
eval("a=3") //Direct call, 在當前作用域執行
}

function bar(){
var b=2
var tmp=eval
tmp("b=3") //Indirect call, 在全局作用域中執行
}

一旦eval被賦值給某個變數,再通過這個變數去執行eval,由於此時eval已經變成了Value,因此會被判定為Indirect call ,而所有的Indirect call都是執行在全局作用域的。

所以老趙寫jscex/Wind.js時曾有一嘆,「 eval是一個無法被封裝的函數 」。


瀉藥

看規範

看規範

看規範

重要事件說三遍

ES5

(反正現在流行一本正經胡說八道答案越長贊越多,可敬貼規範,管他靠不靠的上邊)

15.1.2.1.1 Direct Call to Eval

A direct call to the eval function is one that is expressed as a CallExpression that meets the following two conditions:

The Reference that is the result of evaluating the MemberExpression in the CallExpression has an environment record as its base value and its reference name is "eval".

The result of calling the abstract operation GetValue with that Reference as the argument is the standard built-in function defined in 15.1.2.1.

10.4.2 Entering Eval Code

The following steps are performed when control enters the execution context for eval code:

1. If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then,

a. Initialise the execution context as if it was a global execution context using the eval code as C as described in 10.4.1.1.

2. Else,

a. Set the ThisBinding to the same value as the ThisBinding of the calling execution context.

b. Set the LexicalEnvironment to the same value as the LexicalEnvironment of the calling execution context.

c. Set the VariableEnvironment to the same value as the VariableEnvironment of the calling execution context.

3. If the eval code is strict code, then

a. Let strictVarEnv be the result of calling NewDeclarativeEnvironment passing the LexicalEnvironment as the argument.

b. Set the LexicalEnvironment to strictVarEnv.

c. Set the VariableEnvironment to strictVarEnv.

4. Perform Declaration Binding Instantiation as described in 10.5 using the eval code.

15.1.2.1 eval (x)

When the eval function is called with one argument x, the following steps are taken:

1. If Type(x) is not String, return x.

2. Let prog be the ECMAScript code that is the result of parsing x as a Program. If the parse fails, throw a SyntaxError exception (but see also clause 16).

3. Let evalCtx be the result of establishing a new execution context (10.4.2) for the eval code prog.

4. Let result be the result of evaluating the program prog.

5. Exit the running execution context evalCtx, restoring the previous execution context.

6. If result.type is normal and its completion value is a value V, then return the value V.

7. If result.type is normal and its completion value is empty, then return the value undefined.

8. Otherwise, result.type must be throw. Throw result.value as an exception.

意思就是如果直接調用就綁定當前上下文執行

否則是全局上下文執行

所以

var bar = eval;

bar("...");

bar 不是 直接 eval 調用

直接找到全局下 foo


javascript權威指南第83頁面,到86頁有詳盡的解釋!

使用別名調用eval是全局的,別名間接的改了全局的值,執行的是未更改的局部變數


其實這種情況下根據表現結果一般可以推斷出

eval 自調用改變局部

引用 改變全局

然後你就可以去找一下官方說法驗證一下自己的想法


區分調用和執行作用域


上面答得差不多了,其實0,eval(...)也是這個效果。


那我還是找權威指南看看吧


推薦閱讀:

有關JS中作用域的問題,全局作用域中的變數不應該在全局變數對象中嗎,為什麼下面的函數搜索不到全局變數?
為什麼ECMAScript不原生支持重載?
這種所有圖片一直佔據100%寬度的響應式是如何做的?
有沒優雅的寫法,讓nodejs的回調+循環不那麼操蛋?
如何評判這篇文章說MVVM是一幫沒學好分層的搞出來的?

TAG:JavaScript | eval |