先來看個(gè)栗子:
function(){} // Uncaught SyntaxError: Unexpected token (
(function(){}) // ? (){}
function f(x){ return x + 1 }() // Uncaught SyntaxError: Unexpected token )
function f(x){ return x + 1 }(1) // 1
解釋:
第一行代碼:因?yàn)镴avaScript 將 function 關(guān)鍵字當(dāng)作一個(gè) 函數(shù)聲明語句 的開始常侣,而函數(shù)聲明語句 function 關(guān)鍵字后面應(yīng)該是 函數(shù)名,這里后面跟圓括號(hào)涡贱,當(dāng)然會(huì)報(bào)錯(cuò)。
第二行代碼:給它加上一對(duì)圓括號(hào),解析器會(huì)把()里的當(dāng)做表達(dá)式去解析,在這里就會(huì)當(dāng)做匿名函數(shù)表達(dá)式解析坑夯,所以不會(huì)報(bào)錯(cuò)。
第三行代碼:在一條語句后面加上()會(huì)被當(dāng)做分組操作符抡四,分組操作符里必須要有表達(dá)式柜蜈,所以這里報(bào)錯(cuò);
第四行代碼:在一條函數(shù)聲明語句后面加上(1)指巡,僅僅是相當(dāng)于在聲明語句之后又跟了一條毫無關(guān)系的表達(dá)式淑履,等價(jià)于下面代碼:
function f(x){ return x + 1 }
(1) // 1
表達(dá)式:
js 中的一個(gè)短語,js 解釋器會(huì)將其計(jì)算出一個(gè)結(jié)果藻雪。程序中的常量是最簡單的一類表達(dá)式秘噪。
變量名也是一種簡單的表達(dá)式,它的值就是賦值給變量的值勉耀。復(fù)雜表達(dá)式是由簡單表達(dá)式組成的指煎。
比如,數(shù)據(jù)訪問表達(dá)式是由一個(gè)表示數(shù)組的表達(dá)式便斥、左方括號(hào)至壤、一個(gè)整數(shù)表達(dá)式和右方括號(hào)構(gòu)成。它們所組成的新的表達(dá)式的運(yùn)算結(jié)果是該數(shù)組的特定位置的元素值枢纠。
同樣的像街,函數(shù)調(diào)用表達(dá)式由一個(gè)表示函數(shù)對(duì)象的表達(dá)式和0個(gè)或多個(gè)參數(shù)表達(dá)式構(gòu)成。
將簡單表達(dá)式組合成復(fù)雜表達(dá)式最常用的方法就是使用運(yùn)算符(opetator)晋渺。
運(yùn)算符按照特定的運(yùn)算規(guī)則對(duì)操作數(shù)(通常是兩個(gè))進(jìn)行運(yùn)算宅广,并計(jì)算出新值。
乘法運(yùn)算符“”是比較簡單的例子些举。表達(dá)式xy是對(duì)兩個(gè)變量表達(dá)式x和y進(jìn)行運(yùn)算并得出結(jié)果。有時(shí)我們更愿意說運(yùn)算符返回了一個(gè)值而不是“計(jì)算”出了一個(gè)值俭厚。
語句:
js 整句或命令户魏。js 語句是以分號(hào)結(jié)束;表達(dá)式計(jì)算出一個(gè)值挪挤,但語句用來自行以使某件事發(fā)生叼丑。
“使某件事發(fā)生”的一個(gè)方法是計(jì)算帶有副作用的表達(dá)式。
諸如賦值和函數(shù)調(diào)用這些有副作用的表達(dá)式扛门,是可以作為單獨(dú)的語句的鸠信,這種把表達(dá)式當(dāng)做語句的用法也稱作表達(dá)式語句(expression statement)。類似的語句還有聲明語句(declaration statement)论寨,聲明語句用來聲明新變量或定義新函數(shù)星立。
翻譯一下Axel關(guān)于表達(dá)式和語句的文章:
1. 語句和表達(dá)式
Javascript區(qū)分表達(dá)式和語句爽茴。一個(gè)表達(dá)式產(chǎn)生一個(gè)值且可以被放置在需要一個(gè)值的地方,例如作為一個(gè)函數(shù)調(diào)用中的一個(gè)參數(shù)绰垂。下面的每行都包含一個(gè)表達(dá)式:
myvar
3 + x
myfunc("a", "b")
大致地說室奏,一個(gè)語句執(zhí)行一個(gè)動(dòng)作。循環(huán)和if語句就是例子劲装。 一個(gè)程序基本是語句的序列(我們?cè)谶@里忽略聲明)每當(dāng)JavaScript期待一個(gè)語句胧沫,你也可以寫一個(gè)表達(dá)式。這樣的表達(dá)式被稱作表達(dá)式語句占业。反過來則不可行:你不能在JavaScript需要表達(dá)式的地方寫語句绒怨。例如,一個(gè)if語句不能成為一個(gè)函數(shù)的參數(shù)谦疾。
2. 類似的語句和表達(dá)式
當(dāng)我們觀察這兩種句法類型中的相似成員時(shí)南蹂,兩者的區(qū)別會(huì)更加清晰。
2.1 If 語句 vs 條件運(yùn)算符
下面是一個(gè)if語句的栗子:
if (y >= 0) {
x = y;
} else {
x = -y;
}
表達(dá)式有一個(gè)類似的餐蔬,條件操作符. 上面的語句等價(jià)于下面的語句:
var x = (y >= 0 ? y : -y);
=和;之間的代碼是一個(gè)表達(dá)式碎紊。括號(hào)不是必須的,但這樣條件操作符更易讀樊诺。
2.2 分號(hào) vs 逗號(hào)操作符
在JavaScript中仗考,我們使用分號(hào)來連接語句:In JavaScript, one uses the semicolon to chain statements:
foo(); bar()
對(duì)于表達(dá)式,這里有不出名的逗號(hào)運(yùn)算符:
foo(), bar()
這個(gè)運(yùn)算符計(jì)算兩個(gè)表達(dá)式词爬,并返回第二個(gè)表達(dá)式的結(jié)果秃嗜。栗子:
> "a", "b"
'b'
> var x = ("a", "b");
> x
'b'
> console.log(("a", "b"));
b
3. 看起來像語句的表達(dá)式
Some expressions look like statements. We’ll examine why that is a problem at the end of this section.
3.1 對(duì)象字面量 vs 塊
下面是一個(gè)對(duì)象字面量object literal,一個(gè)產(chǎn)生一個(gè)對(duì)象的表達(dá)式:
{
foo: bar(3, 5)
}
然而顿膨,這也是一個(gè)完美符合規(guī)則的語句锅锨,它有著這些組件:
- 一個(gè)塊:花括號(hào)包裹的語句序列。
- 一個(gè)標(biāo)簽label: 你可以在任何語句前面加上一個(gè)標(biāo)簽前綴恋沃,在這里標(biāo)簽是foo必搞。
- 一個(gè)語句: 表達(dá)式語句bar(3, 5)松靡。
{} 作為一個(gè)塊或者是一個(gè)對(duì)象字面量取決于下面的WAT規(guī)則:
> [] + {}
"[object Object]"
> {} + []
0
+運(yùn)算符是可互換的杭抠,那么這兩個(gè)(表達(dá)式)語句不應(yīng)該返回相同的結(jié)果嗎院促?不佩谷,因?yàn)榈诙€(gè)語句等價(jià)于一個(gè)+在后的代碼塊:
> +[]
0
// 而[].toString()等于"", {}.toString()等于[object Object]夏志,所以第一個(gè)語句得到上述結(jié)果楼誓。
關(guān)于這里的細(xì)節(jié)部分和其他規(guī)則凡桥,請(qǐng)參考What is {} + {} in JavaScript?
JavaScript有單獨(dú)的塊技羔? JavaScript有可以單獨(dú)存在的代碼塊(與作為循環(huán)或if語句的一部分相反)户辞,對(duì)此你可能會(huì)感到驚訝泌类。下面的代碼表明這種塊的一個(gè)用例:你可以給它們一個(gè)標(biāo)簽,并從它們當(dāng)中break出去底燎。.
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 函數(shù)表達(dá)式 vs. 函數(shù)聲明
下面是一個(gè)函數(shù)表達(dá)式(注意這直接作為語句是不合法的刃榨,JavaScript會(huì)把它識(shí)別為函數(shù)聲明弹砚,聲明必須有函數(shù)名):
function () { }
你也可以給函數(shù)表達(dá)式一個(gè)名字,把它變成命名函數(shù)表達(dá)式named function expression:
function foo() { }
函數(shù)名foo只存在于函數(shù)內(nèi)部喇澡,并且比如說迅栅,可以用來自身遞歸:
> var fac = function me(x) { return x <= 1 ? 1 : x * me(x-1) }
> fac(10)
3628800
> console.log(me)
Uncaught ReferenceError: me is not defined
一個(gè)命名函數(shù)表達(dá)式與一個(gè)函數(shù)聲明(大致可以說是一個(gè)語句)無法區(qū)分的。但它們的影響是不同的:一個(gè)函數(shù)表達(dá)式產(chǎn)生一個(gè)值(一個(gè)函數(shù))晴玖,而一個(gè)函數(shù)聲明執(zhí)行一個(gè)行為——?jiǎng)?chuàng)造一個(gè)變量读存,變量的值為這個(gè)函數(shù)。除此之外呕屎,只有函數(shù)表達(dá)式能被立即執(zhí)行(調(diào)用)让簿,函數(shù)聲明則不行。
4. 使用對(duì)象字面量和函數(shù)表達(dá)式作為語句
我們了解到一些表達(dá)式無法和語句作區(qū)分秀睛。那意味著同樣的代碼在表達(dá)式上下文和語句上下文中執(zhí)行會(huì)有不同的效果尔当。一般而言這兩個(gè)上下文是有清晰的區(qū)分的。然而蹂安,對(duì)于表達(dá)式語句椭迎,存在一個(gè)交疊overlap:在一個(gè)語句上下文中存在表達(dá)式。為了避免歧義田盈,JavaScript語法禁止表達(dá)式語句以花括號(hào)或function開頭:
ExpressionStatement :
[lookahead ? {"{", "function"}] Expression ;
那么如果你實(shí)在是想以這兩種方式開頭來寫一個(gè)表達(dá)式語句畜号,怎么辦?你可以把它放進(jìn)括號(hào)中允瞧,這不會(huì)改變運(yùn)行的結(jié)果简软,卻能確保它存在于一個(gè)表達(dá)式-only的上下文中。我們來看兩個(gè)例子:eval和立即執(zhí)行函數(shù)表達(dá)式述暂。
4.1 eval
eval在語句上下文中解析它的參數(shù)痹升。如果你想讓eval返回一個(gè)對(duì)象,你必須用括號(hào)將對(duì)象字面量包裹起來(不輸入引號(hào)的話就不用括號(hào)包裹)畦韭。
> eval("{ foo: 123 }")
123
> eval("({ foo: 123 })")
{ foo: 123 }
> eval({ foo: 123 })
{ foo: 123 }
4.2 立即執(zhí)行函數(shù)表達(dá)式Immediately invoked function expressions (IIFEs)
下面的代碼是一個(gè)立即執(zhí)行函數(shù)表達(dá)式:
> (function () { return "abc" }())
'abc'
如果忽略外面的括號(hào)疼蛾,會(huì)報(bào)語法錯(cuò)誤(函數(shù)聲明不能是匿名的):
> function () { return "abc" }()
SyntaxError: function statement requires a name
實(shí)際運(yùn)行的報(bào)錯(cuò): Uncaught SyntaxError: Unexpected token (
如果你在function和括號(hào)之間添加一個(gè)函數(shù)名,還是會(huì)報(bào)語法錯(cuò)誤(函數(shù)聲明不能被立即調(diào)用):
> function foo() { return "abc" }()
SyntaxError: syntax error
實(shí)際運(yùn)行的報(bào)錯(cuò):Uncaught SyntaxError: Unexpected token )
還有一種確保一個(gè)表達(dá)式在表達(dá)式上下文中被解析的方法艺配,即一個(gè)一元運(yùn)算符据过,例如+或者!或者!!。但是妒挎,與括號(hào)相反的是,這些運(yùn)算符會(huì)對(duì)表達(dá)式的結(jié)果造成影響西饵。如果你不需要多產(chǎn)生的結(jié)果的話酝掩,也算海星:
> +function () { console.log("hello") }()
hello
NaN
> !function () { console.log("hello") }()
hello
true
> !!function () { console.log("hello") }()
hello
false
NaN是對(duì)undefined,即函數(shù)的返回值進(jìn)行+運(yùn)算的結(jié)果眷柔。Brandon Benvie 提出了另一個(gè)可用的一元運(yùn)算符void:
> void function () { console.log("hello") }()
hello
undefined
4.3 立即執(zhí)行函數(shù)表達(dá)式的串聯(lián)
當(dāng)你要串聯(lián)立即執(zhí)行函數(shù)時(shí)期虾,你必須要小心不要忘了分號(hào):
> (function () {}())
(function () {}())
Uncaught TypeError: (intermediate value)(...) is not a function
這段代碼產(chǎn)生一個(gè)錯(cuò)誤原朝,因?yàn)镴avaScript認(rèn)為第二行是企圖獲得調(diào)用作為一個(gè)函數(shù)的第一行的結(jié)果。加一個(gè)分號(hào)就好了:
(function () {}());
(function () {}())
// undefined
對(duì)于一元運(yùn)算符(+既是一元也是二元)镶苞,你可以忽略分號(hào)喳坠,因?yàn)镴avaScript會(huì)自動(dòng)插入分號(hào)。
void function () {}()
void function () {}()
// undefined
JavaScript在第一行后面插入一個(gè)分號(hào)茂蚓,因?yàn)?strong>void不是一個(gè)有效的繼續(xù)語句的方式壕鹉。
Related posts
- What is {} + {} in JavaScript?
- The void operator in JavaScript
- Automatic semicolon insertion in JavaScript
參考文章:
js 表達(dá)式與語句
Expressions versus statements in JavaScript