33-js-concepts之10. 表達(dá)式與語句

先來看個(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

  1. What is {} + {} in JavaScript?
  2. The void operator in JavaScript
  3. Automatic semicolon insertion in JavaScript

參考文章:
js 表達(dá)式與語句
Expressions versus statements in JavaScript

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市聋涨,隨后出現(xiàn)的幾起案子晾浴,更是在濱河造成了極大的恐慌,老刑警劉巖牍白,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脊凰,死亡現(xiàn)場離奇詭異,居然都是意外死亡茂腥,警方通過查閱死者的電腦和手機(jī)狸涌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來最岗,“玉大人帕胆,你說我怎么就攤上這事÷匦裕” “怎么了惶楼?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诊杆。 經(jīng)常有香客問我歼捐,道長,這世上最難降的妖魔是什么晨汹? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任豹储,我火速辦了婚禮,結(jié)果婚禮上淘这,老公的妹妹穿的比我還像新娘剥扣。我一直安慰自己,他們只是感情好铝穷,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布钠怯。 她就那樣靜靜地躺著,像睡著了一般曙聂。 火紅的嫁衣襯著肌膚如雪晦炊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音断国,去河邊找鬼贤姆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛稳衬,可吹牛的內(nèi)容都是我干的霞捡。 我是一名探鬼主播,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼薄疚,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼碧信!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起输涕,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤音婶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后莱坎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體衣式,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年檐什,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碴卧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乃正,死狀恐怖住册,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瓮具,我是刑警寧澤荧飞,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布,位于F島的核電站名党,受9級(jí)特大地震影響叹阔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜传睹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一耳幢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧欧啤,春花似錦睛藻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至倒慧,卻和暖如春按摘,著一層夾襖步出監(jiān)牢的瞬間讥邻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國打工院峡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人系宜。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓照激,卻偏偏與公主長得像,于是被迫代替她去往敵國和親盹牧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子俩垃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,132評(píng)論 0 13
  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,142評(píng)論 0 3
  • 前言 本系列文章主要是基于W3school這個(gè)學(xué)習(xí)網(wǎng)站來總結(jié)的汰寓,之所以會(huì)自己總結(jié)一番口柳,一來是因?yàn)榫W(wǎng)站中的實(shí)例效果,...
    AR7_閱讀 2,898評(píng)論 7 71
  • 標(biāo)簽: 我的筆記 ---學(xué)習(xí)資料:http://javascript.ruanyifeng.com/ 1. 導(dǎo)論 ...
    暗夜的怒吼閱讀 803評(píng)論 0 1
  • 祝福小一輩開啟美好的人生旅程有滑! 珍惜跃闹、寬容、呵護(hù)毛好、支持望艺,這是我對(duì)他倆的贈(zèng)言
    曾子玲閱讀 280評(píng)論 0 1