摘要:主要是因為它們會因為不同的位置產(chǎn)生不同的行為。函數(shù)宣告則產(chǎn)生一個行為,即建立一個變數(shù),然後它的值是一個。而且只有可以被立即調(diào)用,函數(shù)宣告不行。為了防止產(chǎn)生疑義。會禁止使用或開頭。屬於一種,其產(chǎn)生一個值或說回傳一個值,並完成某項任務(wù)。
前言
原文在此,對於 Axel 的文章一直有種雖然短卻難以讀透的感覺。這篇文章是再讀一次的筆記與部份翻譯,如有錯誤歡迎指教。
註: 下面一些範(fàn)例當(dāng)我們在瀏覽器 console 執(zhí)行時,回傳值與程式執(zhí)行的順序在 Chrome 與 Firefox 會有差別。注意一下箭頭符號就知道哪個是 return 了。如下範(fàn)例
> function foo () {console.log("foo");} > foo(); // 在 Chrome 是 foo undefined // 在 Firefox 是 undefined foo1. Statements 述句和 Expressions 表達(dá)式
一直以來,在讀技術(shù)文章的時候您一定不陌生這兩個詞,因為小弟過去對於這種細(xì)枝末節(jié)並不是很重視,加上計算機背景又不深厚。所以對於一些文章和概念的掌握度一直不是很精確。這次重讀一遍 Axel 的文章,希望能夠?qū)?javascript 有更深入的理解。
事實上,在 javascript 中能夠清楚的分辨 expressions 和 statements 的差異對於撰寫程式碼是有一定的幫助,可以避免掉入一些陷阱。
簡單來說一個表達(dá)式 expressions 會產(chǎn)生一個值,我們會在撰寫它的地方期望得到一個值。舉例來說像是調(diào)用 function 中的引數(shù)(arguments),或者指定式 = 的右邊都屬於 expressions 的位置。
參數(shù)(parameters),引數(shù)(arguments)
parameters 即在 function 中定義必須在呼叫程序時傳遞,可以用來取值的符號(變數(shù)名稱)
arguments 是實際呼叫時,傳入的值
下面的每一行都是一個 expression:
myvar 100 + x fn("a", "b")
而大體來說述句 statements 即執(zhí)行動作,完成特定任務(wù)。賦值,迴圈和 if 述句都是 statements 的例子。
在我們實際撰寫的程式碼中到底是怎麼區(qū)分的呢?讓我們看看 MDN 上定義的 if 述句
if (condition) statement1 [else statement2]
要明白這些事情我們得先從 syntax 開始講起,基本上程式是透過一系列規(guī)定好的語法組成,稱為 syntax ,它類似於我們?nèi)祟愓Z言中的文法,不管是中文還是英文。一個程式要遵循著 syntax 並且由一系列 statements 組成的。
javascript 直譯器在解析程式碼時對於語法結(jié)構(gòu),即這些程式碼出現(xiàn)的位置會有對應(yīng)的處理方式。
另外,拿上面的例子來說,任何 javascript 預(yù)期會有 statements 的地方你都可以使用 expressions,例如在 statement1 的地方呼叫一個 function。
這個 function 就稱作 expression statement 屬於一種特殊的 statement ,這個 function 自然可以 return 一個值,同時也可以在內(nèi)部產(chǎn)生一些 side effect,不過如果我們重點擺在一些 side effect 部分時,通常就會回傳 undefined。如下圖
通常一個 statement 是獨立的,只會完成某項任務(wù),不過如果它影響了整個程式例如: 異動了機器內(nèi)部的狀態(tài),或者影響後面的 statement,這些造成的改變我們就稱為 side effect (副作用)
反過來,我們不可以在預(yù)期是 expression 的地方換成 statement。例如我們不可以在 function 的引數(shù)的地方改成 if 述句。
歸納一下關(guān)係如下:
syntax
statements
expression statements
expressions
2. statements 與 expressions 實例讓我們看一下這兩段類似功能的程式碼,我們可能會更加清楚它們之間的分別。
if 條件式語句和條件運算子(三元運算子)var x; if (y >= 0) { x = y; } else { x = -y; }
上面這幾句程式碼無疑都是 statements,另外 expression 也有個對應(yīng)的寫法和上面這段程式碼完全等價
var x = (y >= 0 ? y : -y);
在等號和分號之間的就是一個 expression,其中的 () 不是必須的,但加上去比較容易閱讀。
分號; 與 逗號,在 javascript 中 statement 之間我們可以用 ; 分號來區(qū)分和串連。
foo(); bar()
而 expression 也有一個鮮為人知的 , 運算子可以用來串連。
foo(), bar()
兩個 expression 都會執(zhí)行,但是返回最後面的。
> "a", "b" "b" > var x = ("x", "y") > x "y"3. 容易產(chǎn)生誤會的 expressions (看起來像 statements)
有些 expressions 看起來像是 statements。下面我們會列出一些容易產(chǎn)生疑義的例子逐一討論。主要是因為它們會因為不同的位置產(chǎn)生不同的行為。
物件實字(Object Literal)與程式碼區(qū)塊(Block)下面這段範(fàn)例是一個物件實字,屬於 expression ,用來產(chǎn)生一個物件
{ foo: bar(3, 5) }
Object Literal 是一個透過 {} 與 , 逗號分隔的鍵值對列表就是 var o = {name:"Object"} 這樣的寫法。
同時它還是一個符合規(guī)範(fàn)的 statement,因為它具備了:
block: 一段 statement 放在 {} 中
label: 我們可以在任何一段 statement 之前放上一個 label,在這邊 label 是 foo:
statement: 一個 expression statement bar(3, 5)
所以 {} 到底是一個 block 還是物件實字,你可能會說是物件那讓我們來看看下面這個奇怪的例子
// 在看這個奇怪的範(fàn)例之前讓我們先看看一些 javascript 的行為 // 當(dāng)我們把非數(shù)字相加時 > 1 + "string" "1string" > 1 + undefined NaN > 1 + null 1 > 1 + [2,3,] "12,3" > 1 + {name: "andyyou"} "1[object Object]" // 上面的範(fàn)例我們得知,除了 undefined 和 null,基本上 js 會把物件先 `toString()` 再相加。 > [].toString() "" > [1, 2, 3].toString() "1,2,3" > var o = {}; > o.toString(); "[object Object]" // 有了上面的基礎(chǔ)知識之後,讓我們來看看這令人嚇尿的行為 > [] + {} "[object Object]" // 好!這題如我們所料,[] 產(chǎn)生 "" 加上 {} 產(chǎn)生 "[object Object]" // 先問你個問題: + 兩邊的運算元能不能互換而結(jié)果不變 // 你可能回答: 是!??! // 但.... > {} + [] 0
上面程式碼最後一句的 {} 是一個 block 所以執(zhí)行完之後接 +[]。
> +[] 0
嚇尿了吧!除了 if, 迴圈外 javascript 也具有獨立的 block。
下面這段程式碼說明了 label 和 block 的用法:
function test (printTwo) { printing: { console.log("One"); if (!printTow) break printing; console.log("Two"); } console.log("Three"); }
執(zhí)行的結(jié)果
> test(false) "One" "Three" > test(true) "One" "Two" "Three"
從上面驗證了 {} 的語法如果遇到 statements 的位置,就會被當(dāng)成 statements,而如果在 expressions 的位置就會被當(dāng)解析成一個值。
> {} + []; // 就是一個最好的例子,{} 被當(dāng)作 statement 就是一個 block // 如果換成 > var x = {}; // 那他就是一個 expression 代表一個值 - 一個物件
讓我們接著看下一個例子。
Function expression 與 function 宣告下面的程式碼是一個 function expression
function () {}
你也可以給 function expression 一個名稱
function foo () {}
在當(dāng)作 function expression 時上面的 function 名稱 foo 只存在在 function 內(nèi)部能使用,舉例來說像是一個遞迴。
你可能困惑了,我們到底在說啥?看看下面的例子,我們要說的是當(dāng) function 放在 statements 和 expressions 不同位置時的差異(放在 = 右邊是 expression)
var fn = function me(x) { return x <= 1 ? 1 : x * me(x-1)} // = 等號右邊是一個 expression 的位置 fn(10); // 3628800 console.log(me); // ReferenceError: me is not defined
具名的 function expression 和函數(shù)宣告的寫法看起來是沒有區(qū)別的。但實際上這兩者的效果截然不同,function expression 產(chǎn)生一個值(一個 function)。函數(shù)宣告則產(chǎn)生一個行為,即建立一個變數(shù),然後它的值是一個 function。而且只有 function expression 可以被立即調(diào)用,函數(shù)宣告不行。
從上面這幾點看來能夠區(qū)分 expression 和 statement 挺重要的。
4. 使用物件實字與 function expression 當(dāng)作 statements我們已經(jīng)看到有一些 expression 和 statement 語法上是沒有區(qū)別的。這意味著相同的程式碼會有不同行為取決於它出現(xiàn)在 expression 的位置
或者是 statement 位置。為了防止產(chǎn)生疑義。javascript 會禁止 expression statement 使用 {} 或 function 開頭。
換句話說就是在 javascript 認(rèn)定為 statement 的位置,使用了 expression 會變成 expression statement。這並不是 expression,所以產(chǎn)生一些特殊的狀況 {} 會被當(dāng)作 block 解釋,function 開頭的語法會被當(dāng)作函數(shù)定義。
所你當(dāng)你想要使用這兩者為開頭撰寫 expression statement 時,你可以放上 () 可以確保位於一個 expression 的位置。
這就是 statement 或者 expression 所延伸的問題,也可以說造成我們極度混亂的根源。讓我們來看看 eval 和立即調(diào)用函式:
evaleval 會解析他的引數(shù)當(dāng)做一句 statement。如果你希望 eval 回傳一個物件你就需要在物件實字外圍放上()
> eval("{foo: 123}"); 123 > eval("({foo: 123})"); {foo: 123}
下次再閱讀文件的時候是不是更有感覺了。
立即調(diào)用函式立即調(diào)用函式的部分
> (function () { return "abc" }()) "abc"
如果你省略了 () 如下,你就會得到一個語法錯誤的訊息。function 宣告不可以匿名。
> function () { return "abc" }() SyntaxError: function statement requires a name
就算替 function 加上名稱還是噴錯
> function foo() { return "abc" }() SyntaxError: syntax error
因為函數(shù)宣告不能立即調(diào)用(IIFE),不過除了使用 () 還有些技巧,當(dāng)我們硬要要把某段程式當(dāng)做 expression 執(zhí)行時,可以使用一元運算子 + 或 !,不過和()的方式比起來這會影響回傳結(jié)果,如果你不在意的話這也是一種方式。
> +function () { console.log("hello") }() NaN hello
這個 NaN 是 + 遇上 undefined 的結(jié)果,喔!對了還有一種是透過 void 的方式
> void function () { console.log("hello") }() undefined hello連續(xù)使用立即調(diào)用函式
當(dāng)你在連續(xù)呼叫 IIFE 的時候必須要注意不要忘記分號 ; 結(jié)尾
(function () {}()) (function () {}()) // TypeError: undefined is not a function
上面的程式碼會產(chǎn)生錯誤因為 javascript 以為第二行的 () 是要拿第一行產(chǎn)生的結(jié)果當(dāng)作一個函數(shù)來呼叫。
(function () {}()); (function () {}()) // OK
下面範(fàn)例因為 javascript 自動補上分號的功能,使用一元運算子的話 ; 可以省略。
void function () {}() void function () {}() // OK
javascript 會自動補上分號是因為接在第一行之後的 void 並不是可以接下去的語句(符合規(guī)範(fàn)能串在一起的寫法)。
另外關(guān)於 javascript 自動補上分號有幾項建議如下:
在 return,break,continue,++,-- 五種 statement 中,換行字元可完全等於 ;。
var,if,do while,for,continue,break,return,with,switch,throw,try,debugger 關(guān)鍵字開頭,以及空的 statement,上一行會自動補上分號。
遇到 expression statement 和 function expression 情況非常複雜,後面請務(wù)必要加上分號。
凡 ( 和 [ 開頭的 statements 前面或上一句不加非常危險。
想要更深入明白 ASI,請參考。
總結(jié)syntax : 語法(文法),該怎麼組織 statements 與 expressions。
expressions : 會產(chǎn)生一個值,其意義就是代表一個值的表式例如 x + y。
statements : 完成某項任務(wù)的操作。賦值,條件判斷,宣告都算是 statements; if (condiction) { console.log("WoooW!") }。
expression statements : 屬於一種 statement,其產(chǎn)生一個值(或說回傳一個值),並完成某項任務(wù)。例如:x += 1 或者在 statement 執(zhí)行一個 side effect 的函數(shù)呼叫。
在 statements 位置放入 expressions 要小心(即 expression statement),因為 javascript 對於 expression 和 expression statement 解釋行為是不一樣的。
下面這兩種語法對於其位置尤其需要注意
function
statement 位置: 當(dāng)作函數(shù)宣告,即建立一個變數(shù)它的值是一個 function。,不能立即調(diào)用。
expression 位置: 為 function expression 產(chǎn)生一個為 function 的值,可以被立即調(diào)用(IIFE)。
{}
statement 位置: block 一個程式碼區(qū)塊,例如 for, label 的 block。
expression 位置: 物件實字,建立一個值 - 物件。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/86283.html
摘要:於是其他的東西相加的時候?qū)晦D(zhuǎn)型成數(shù)字或者字串。整個過程即先依據(jù)轉(zhuǎn)換為原始型別,這裡要注意並不是最終結(jié)果,再來依據(jù)需要看是否要再將原生型別轉(zhuǎn)成數(shù)字或字串。這個結(jié)果等於只作的算。 這篇文章源自 What is {} + {} in JavaScript? 其實早在 2012 年就問世了。時至 2016 年末純粹是在聊天時重提這個問題,但由於年紀(jì)大了記憶力不佳,竟然記錯了,所以才會有這一...
摘要:會自動調(diào)用轉(zhuǎn)換函數(shù)將這個表達(dá)式的結(jié)果轉(zhuǎn)換為一個布爾值。語句語句與語句的關(guān)系最為密切,而且也是在其他語言中普遍使用的一種流控制語句。 表達(dá)式在 JavaScript 中是短語,那么語句就是整句命令。表達(dá)式用來計算出一個值,語句用來執(zhí)行以使某件事發(fā)生。從本質(zhì)上看,語句定義了 JavaScript 中的主要語法,語句通常使用一或多個關(guān)鍵字來完成給定任務(wù)。語句可以很簡單,例如通知函數(shù)退出;也可...
摘要:表達(dá)式用來計算出一個值,語句用來執(zhí)行以使某件事發(fā)生。其中,語句會立即退出循環(huán),強制繼續(xù)執(zhí)行循環(huán)后面的語句。在執(zhí)行語句之后,結(jié)果顯示。語句語句的作用是指定函數(shù)調(diào)用后的返回值。語句語句的作用是把程序運行時產(chǎn)生的錯誤顯式地拋出異常。 表達(dá)式在 JavaScript 中是短語,那么語句就是整句命令。表達(dá)式用來計算出一個值,語句用來執(zhí)行以使某件事發(fā)生。從本質(zhì)上看,語句定義了 JavaScript...
摘要:花點時間搞清楚中的分號規(guī)則吧不管你喜歡結(jié)尾帶分號或省略分號的模式分號允許的場景分號一般允許出現(xiàn)在大部分語句的末尾,比如等栗子僅有一個分號可以表示空語句在中合法,比如可解析為三個空語句空語句可用于輔助產(chǎn)生語法合法的解析結(jié)果,如如果沒有末尾的 花點時間搞清楚JS中的分號規(guī)則吧~~~不管你喜歡結(jié)尾帶分號或省略分號的模式 分號允許的場景 分號一般允許出現(xiàn)在大部分語句(statement)的末尾...
摘要:是一項標(biāo)準(zhǔn),于年月獲得批準(zhǔn)。靜態(tài)限制在分配之前阻止使用。使用通用到基于自定義迭代器的迭代。迭代基于這些鴨子類型接口僅使用類型語法進(jìn)行展示生成器生成器使用和簡化迭代器。生成器是迭代器的子類型,包括額外的和。 ECMAScript 2015是一項ECMAScript標(biāo)準(zhǔn),于2015年6月獲得批準(zhǔn)。 ES2015是該語言的重要更新,也是自2009年ES5標(biāo)準(zhǔn)化以來該語言的第一次重大更新?,F(xiàn)在...
閱讀 1875·2021-11-24 10:21
閱讀 1304·2021-09-22 15:25
閱讀 3224·2019-08-30 15:55
閱讀 766·2019-08-30 15:54
閱讀 3527·2019-08-30 14:20
閱讀 1713·2019-08-30 14:06
閱讀 692·2019-08-30 13:11
閱讀 3244·2019-08-29 16:43