1.語句和運算式
JavaScript中的運算式和語句是有區別的.一個運算式會產生一個值,它可以放在任何需要一個值的地方,比如,作為一個函數調用的參數.下面的每行代碼都是一個運算式:
myvar3 + xmyfunc("a", "b")語句可以理解成一個行為.迴圈語句和if語句就是典型的語句.一個程式是由一系列語句組成的.JavaScript中某些需要語句的地方,你可以使用一個運算式來代替.這樣的語句稱之為運算式語句.但反過來不可以:你不能在一個需要運算式的地方放一個語句.比如,一個if語句不能作為一個函數的參數.
2.其他文法
看看下面這兩對類似的文法,搞懂這些後,能夠協助我們更好的理解語句和運算式之間的關係.
2.1 If語句和條件運算子
下面是一個if語句的例子:
複製代碼 代碼如下:var x;
if (y >= 0) {
x = y;
} else {
x = -y;
}
類似if語句功能的運算式叫做條件運算子.上面的語句等價於下面的.
var x = (y >= 0 ? y : -y);
在等號=和分號;之間的代碼就是條件運算式.兩邊的小括弧不是必需的,但我覺得小括弧能讓條件運算式更易讀.
2.2 分號和逗號運算子
在JavaScript中,使用分號可以串連兩個語句:
foo(); bar()要想串連兩個運算式,使用的是不常見的逗號運算子:
foo(), bar()逗號運算子會計算前後兩個運算式,然後返回右邊運算式的計算結果.例如: 複製代碼 代碼如下:> "a", "b"
'b'
> var x = ("a", "b");
> x
'b'
> console.log(("a", "b"));
3.看似語句的運算式
一些運算式看起來像是語句,這可能會帶來一些麻煩.
3.1 對象字面量和語句塊
下面是一個對象字面量,也就是一個可以產生一個對象值的運算式. 複製代碼 代碼如下:{
foo: bar(3, 5)
}
不過同時,它也是一個完全合法的語句,這個語句的組成部分有:
•一個代碼塊:一個由大括弧包圍的語句序列.
•一個標籤:你可以在任何語句前面放置一個標籤.這裡的foo就是一個標籤.
•一條語句:運算式語句bar(3, 5).
你也許會感到震驚,那就是JavaScript居然可以有獨立的代碼塊(常見的代碼塊是依託於迴圈或者if語句的).下面的代碼示範了這種代碼塊的作用:你可以給它設定一個標籤然後跳出這個代碼塊. 複製代碼 代碼如下:function test(printTwo) {
printing: {
console.log("One");
if (!printTwo) break printing;
console.log("Two");
}
console.log("Three");
}
> test(false)
One
Three
> test(true)
One
Two
Three
3.2 函數運算式和函式宣告
下面的代碼是一個函數運算式:
function () { }你還可以給這個函數運算式起一個名字,將它轉變為一個命名(非匿名)的函數運算式:
function foo() { }這個函數的函數名(foo)只存在於函數內部,比如,可以用它來做遞迴運算:
複製代碼 代碼如下:> var fac = function me(x) { return x <= 1 ? 1 : x * me(x-1) }
> fac(10)
3628800
> console.log(me)
ReferenceError: me is not defined
一個命名的函數運算式從表面上看起來,和一個函式宣告並沒有什麼區別.但他們的效果是不同的:一個函數運算式產生一個值(一個函數).一個函式宣告執行一個動作:將一個函數賦值給一個變數. 此外,只有函數運算式可以被立即調用,函式宣告不可以.
3.3 解決衝突
從3.1和3.2可以看出,有些運算式和語句在表面上看不出有什麼區別.也就意味著,相同的代碼,出現在運算式上下文和出現在語句上下文會表現出不同的作用.通常情況下,這兩種上下文是沒有交集的.但是,如果是運算式語句的話,會有一個重疊:也就是說,會有一些運算式出現在語句上下文上.為瞭解決這種歧義,JavaScript文法禁止運算式語句以大括弧或關鍵字"function"開頭: 複製代碼 代碼如下:ExpressionStatement :
[lookahead ∉ {"{", "function"}] Expression ;
那麼,如果你想寫一個以那些標誌開頭的運算式語句,該怎辦呢? 你可以把它放在一個括弧內部,這樣並不會改變運行結果,只會確保該運算式被解析在運算式上下文中.讓我們看兩個例子.第一個例子:eval會按照語句上下文解析它的參數.如果你想讓eval返回一個對象,你必須在對象字面量兩邊加上一個括弧. 複製代碼 代碼如下:> eval("{ foo: 123 }")
123
> eval("({ foo: 123 })")
{ foo: 123 }
第二個例子:下面的例子是一個立即執行的函數運算式. 複製代碼 代碼如下:> (function () { return "abc" }())
'abc'
如果你省略了小括弧,你會得到一個語法錯誤(函式宣告不可以是匿名的): 複製代碼 代碼如下:> function () { return "abc" }()
SyntaxError: function statement requires a name
如果你添加上函數名,還會得到一個語法錯誤(函式宣告不能被理解執行): 複製代碼 代碼如下:> function foo() { return "abc" }()
SyntaxError: syntax error
另外一個能讓運算式在運算式上下文上被解析的辦法是使用一元運算子,比如 + 或者 !.但是,和使用括弧不同的是,這些操作符會改變運算式的運行結果.如果你不關心結果的話,完全可以使用: 複製代碼 代碼如下:> +function () { console.log("hello") }()
hello
NaNNaN
是+作用在函數執行後的傳回值undefined上的結果.
譯者注:我覺的沒翻譯明白,所以用拙劣的水平畫了張圖.
原文(英文):http://www.2ality.com/2012/09/expressions-vs-statements.html