JS立即執行如何使用?還是說圓括弧本身是用來改變JS的執行上下文環境?

首先,根據JS高級程序設計中第七章提到(P185,第四版),函數聲明後面無法跟圓括弧,只有表達式可以。但是,這段代碼:

前幾個是將函數聲明轉換為表達式 不會報錯,但是function(){return "hello world";}() 本身會報錯,但是小括弧放在外面 ( function(){return "hello world";}() )卻也不會報錯?函數應該執行外層嵌套的括弧 中裡面的內容吧,可是裡面的內容本身就會報錯啊。

再舉個栗子:eval()將Json轉換為JS對象需要使用小括弧,這個是因為什麼原因?

//for example
eval("{ foo: 123 }")
123
&> eval("({ foo: 123 })")
{ foo: 123 }

所以個人推測是JS中圓括弧本身會改變JS執行上下文....

補充資料和來源:為什麼JS中eval處理JSON數據要加括弧_javascript技巧

eval解析JSON字元串的一個小問題

[譯]JavaScript中:表達式和語句的區別


瀉藥

再次強調

沒事兒時候去看看規範

先說 eval 這個

由於 {} 並不僅僅表示對象直接量

還表示語法塊

其中的 key: 也不僅僅為 object·的屬性名定義

還有 label 語法聲明也是如此

所以

{ foo: 123 }

會表示為語法塊中含有 名為 foo 的 label

其後值數字直接量

而不是對象直接量表示

加入括弧

({ foo: 123 })

語法就不同了

括弧內需要按照表達式解析

所以裡頭得是 Expression而不能是 Statement

ObjectLiteral 就在基本表達式中

此時的{xx:xxx}就會被解析為對象直接量而不是語法塊+label

完全與你理解的什麼改變執行上下文沒關係在說 function

其實同理的

FunctionDeclaration 與 Statement 同級別

所以直接寫

function(){}

會被優先解釋為FunctionDeclaration

而非FunctionExpression

雖然他們很像

就差個 Identifier (FunctionExpression 的 Identifier 標註是 opt 可選的)

此時 function(){} 會被認為是 FunctionDeclaration 但是缺少 Identifier 標示符而被認為是語法錯誤

後面跟上() 也不過是

function(){}
()

這樣了

第一行函數定義就語法錯誤

所以不能執行

而FunctionExpression之所以可以被成功進行語法解析

是因為他得放在該放的地方

就是 Expression 之中才行

前面說了() 里是啥

是個 Expression 啊

(function(){})()

也好

(function(){}())

也好

裡面的function(){}均為函數表達式而不是函數定義

同理

只要在 Expression 里的都行

+function(){}()
-function(){}()
!function(){}()
....

等等都可以

只因為 + - !等後面是緊跟 一元表達式 (UnaryExpression)

一元表達式又屬於 Expressions

甚至

if (function(a){return a &> 1}(2)) {

alert("hehehe")

}

switch (function(a){return a - 5}(8)) {

case function(b){return b - 2}(5) :

alert("hehehe")

}

只要在 Expressions 里

都是可以被當做函數表達式解析的

同跟你理解的什麼改變執行上下文沒關係

不過項目里你可千萬別跟上頭這麼寫

會被別人打死……


你說的這些都是語法解析造成的,跟運行、context 沒一毛錢關係。


我覺得吧, ( function(){return "hello world";}() )最外面的括弧()起到運算符的作用,跟!、+、-、=一樣,把函數聲明轉換成函數表達式了。


我來強行跑個題露個臉。在 @貘吃饃香 老師的回答的基礎上,說明一下我對函數表達式一個細節的理解。

摘自我在SegmentFault上的一個回答。

-------------------------------- 聽說要來個分割線--------------------------------

var f = function g(){ return 23; }; typeof g(); 報錯,而不是「number」

var f = function g(){ return 23; }; typeof g();

為什麼是報錯,而不是「number」,

強行回答一個~ 補充一下@manxisuo 的答案。

(以下截圖均來自ES5標準文檔,內容是本人閱讀後的理解)

首先,我們知道函數的定義有兩種方式:

  • 函數聲明 (FunctionDeclaration)

  • 函數表達式 (FunctionExpression)

函數聲明在程序 (Program) 中有著非比尋常的優先順序:

一段ES程序就是由語句 (Statement) 和函數聲明組成的。

顯然 ``var f = function g(){ return 23; };`` 是一段語句,而非函數聲明。具體來說,是一段變數語句 (VariableStatement) 。等號右側被解釋為賦值語句(AssignmentExpression) ,再經過一系列的解釋後,被確定為函數表達式。等號左側變數將被賦值為右側函數表達式解釋執行後的引用。

因此問題的關鍵在於函數表達式是如何解釋執行的。

再看函數表達式的語法,函數名是一個可選項。而函數名的有無,函數表達式的解釋執行步驟有著巨大的差異。

function ( FormalParameterListopt ) { FunctionBody }

當沒有函數名時,函數表達式的解釋執行與函數聲明相似,作用域即為當前執行的詞法環境。後者,函數聲明會執行一個抽象方法CreateMutableBinding,在環境記錄項中創建一個新的可變綁定(即變數)。從這裡可以知道,新創建的變數就在當前的作用域內。

function Identifier ( FormalParameterListopt ) { FunctionBody }

重點來了,指定了函數名的函數表達式,會首先執行一個抽象方法NewDeclarativeEnvironment,該方法創建一個空的新詞法環境,並把 當前的執行環境 引用為 新的詞法環境的外部詞法環境。然後以新的詞法環境為作用域,執行了接下來的步驟,並最後將函數的引用交給左側的變數。因此這裡的函數名,是綁定在新的詞法環境中的,外部環境也就無法找到函數名,拋出了ReferenceError。

另外@manxisuo 所說,也同樣可以解釋了。

以上。

Reference

- ECMA-262標準文檔

- ES5/函數定義


eval一個對象需要加上括弧是因為如果不加上括弧的話,會被當成是一個代碼塊,在代碼塊裡面寫foo: 123就不是對象。

函數表達式才能立即執行,函數聲明不能,上面的!、()讓函數聲明變成函數表達式,所以能立即執行。


人家的解釋器就那麼寫的,就那麼定義的,跟上下文沒關係,參考編譯原理的基礎知識,詞法分析,語法分析,語義分析等等,分清代碼與程序的界限


第一個報錯是解析出錯,瀏覽器不知道你寫的是個啥。eval不加圓括弧一樣可以運行,但是沒有任何返回,沒有意義,加括弧返回的就是對象。


推薦閱讀:

js 中,不使用數組,不使用對象,可以 return 多組值嗎?
TypeScript 的命運會不會和 CoffeeScript 一樣 ?
如何看待阿里開源的Rax框架?
瀏覽器端js有如何為本機生成固定的uuid?
有沒有必要將 DOM 結構緩存到本地?如果有,意義是什麼?

TAG:前端開發 | JavaScript | JavaScript語言精粹 |