1. 程式人生 > >JavaScript中:表達式和語句的區別

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

運算 false 第一個 ron 為我 error 直接 -- 組成

1.語句和表達式

JavaScript中的表達式和語句是有區別的.一個表達式會產生一個值,它可以放在任何需要一個值的地方,比如,作為一個函數調用的參數.下面的每行代碼都是一個表達式:

myvar
3 + x
myfunc("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"));
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

  一個命名的函數表達式從表面上看起來,和一個函數聲明並沒有什麽區別.但他們的效果是不同的:一個函數表達式產生一個值(一個函數).一個函數聲明執行一個動作:將一個函數賦值給一個變量. 此外,只有函數表達式可以被立即調用,函數聲明不可以.

  只要是表達式語法,腳本宿主就認為 function 是一個直接量函數,如果什麽都不加,光以 function 開頭的話則認為是一個函數聲明,

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
NaN

NaN+作用在函數執行後的返回值undefined上的結果.

譯者註:我覺的沒翻譯明白,所以用拙劣的水平畫了張圖.

技術分享

    ---------------------------第二中理解-------------------- 表達式(expressions)和語句(statements)在javascript非常常見,但是就是這些常見的元素,有時候我 們也未必能夠正確的領會其要表示的含義和用法。這是因為我們總是對常見的東西本能的表示默認,好像它天生就該如此,為很少去考慮其背後所代表的含義。比 如:if的條件中為什麽能有賦值,立即執行函數為什麽要用小括號給括起來調用等。

  在區分表達式和語句之前,我們先分別對他們進行介紹:

  1.表達式(expressions)

    表達式是由運算符構成,並運算產生結果的語法結構。每個表達式都會產生一個值,它可以放在任何需要一個值的地方,比如,作為一個函數調用的參數.下面的每行代碼都是一個表達式:

var a = (5 + 6) / 2; //表達式:(5 + 6) / 2
var b = (function(){ return 25;})(); //表達式: (function(){ return 25;})()
foo(a*b); //表達式:a*b

  2.語句(statements)

    語句則是由“;(分號)”分隔的句子或命令。如果在表達式後面加上一個“;”分隔符,這就被稱為“表達式語句”。它表明“只有表達式,而沒有其他語法元素的語句”。

var a = (5 + 6) / 2; //整行,賦值語句
if(a>12) { statements} //條件語句
var o = {}; //賦值語句
(function(obj){ obj.b = 23;})(o||{}); //表達式語句

  一般的javascript中的語句分為下面幾種:

  (1)聲明語句:變量聲明和函數聲明

  (2)賦值語句

  (3)控制語句:能夠對語句執行順序產生改變,包括條件語句和循環語句,當然還有比較特殊的標簽語句。

  (4)表達式語句:這些語句去掉最後分號,都也可當表達式用的。常見的有:對象操作(new、delete)、函數調用(函數執行,必有返回值)等。

var num = 9; //聲明、賦值語句
vloop: //標簽語句
{    //其實這裏大括號可以不需要的,在這裏我只想向大家展示一下這種代碼塊結構而已
     for(var i=1; i<10; i++) { //循環語句
            if(i==num){ //條件語句
                  break vloop;
            }else{
                  num = num - 1; 
            }      
     }  
}     
console.log(num); //表達式語句,輸出:5 

  由上面可以看出,表達式和語句還是存在很大區別的,可也說表達式是語句的構成部分,而語句則又可以組成可執行代碼塊。一般而已,我們都可以很直觀的看出兩者的區別,但是,一些特殊情況就不太好區別。

難以區分的表達式和語句

 1.對象字面量和代碼塊

var o = {
     a : {},
     b : "string"    
}

上面是一個簡單至極的對象字面量,但是我們單單從代碼的直觀層面來看,這個字面量其實跟代碼塊非常相似,由兩個標簽語句組成的感覺。復雜些上,還有上面之 前我在語句最後舉得那個例子,例子中代碼塊位於標簽語句下面,裏面包含有個for循環。這時候,你說這個由{}構建的代碼塊是表達式呢還是語句?

  2.命名函數表達式

  javascript中有三種函數類型:函數聲明,函數表達式和函數構造器創建的函數。

  (1)函數聲明(FD)

  function foo(){ statements; }

  (2)函數表達式(FE)

  var foo = function(){ statements;}

  還有一種比較特殊點的:var foo = function _foo() { statements;} ,這是時候,給函數一個供內部使用的名字_foo,所有,此時,這種類型又稱:命名函數表達式(NFE)。

  (3)函數構造器創建

  var foo = new Function(expressions);

  其實上面說了函數的三種類型並不是我們這章的主要重點,這這我們也是探討一下FD和NFE的一些區別而已,關於其他函數內容後面我單獨在細說。

  是不是看到FD和NFE的形式之後,又有點點迷糊了,NFE除了前面多了一個var和變量名之外,其他和FD的結構一模一樣,這樣的話,那是不是說明FD既可以作聲明語句,也同時可以作賦值表達式呢?

  解答之前兩個疑惑

  鑒於上面兩個比較讓人迷惑的語法點,javascript它自己也認識到不足,之後果斷改進,做出了如下聲明:JavaScript語法禁止表達式語句以大括號或關鍵字"function"開頭。

知錯能改善莫大焉,當知道javascript做出如此強制的規約時,就一下子對前面兩個疑惑有了答案。

  在這之前,我們還要想提下三個概念:語句上下文、表達式上下文和表達式語句。

  語句上下文:在這個環境中,這這句代碼(表達式或者語句)應該理解成一個語句。

  表達式上下文:在這個環境中,這句代碼(表達式或者語句)應該理解成一個表達式。

  表達式語句:這個表達式既可以看作是一個表達式(因為它能產生一個值),又可以看作是一個執行語句(因為它能執行某些功能,比如執行一個函數等)。表達式語句可以是javascript鏈式編程的基礎。

  上面這個概念起輔助理解作用,不用做過深追究。

  我們再來看之前的那兩個疑惑:

  第一個,vloop的冒號後面由大括號中那一段代碼,裏面都有循環和賦值等操作,那說明它都不是一個表達式語句,所以它不必遵循上面的規定。在 這其實它只是一個代碼塊語句而已。不過對於對象字面量而言,它確實一個貨真價實的表達式語句,根據規約,它就只能乖乖的做表達式,不能做語句。

  第二個,對於NFE類型函數來說,你可以將它看作是一個函數申明語句,同時也可以將之看成一個表達式,但是根據javascript的規定,表 達式語句不能以function開頭,所有在這,NFE中的肯定是表達式了。當然,對於FD,這個很明顯,是函數聲明語句,不用懷疑。

  其實,還有另一種判定方法,根據上下文判斷,利用之前我們說過語句上下文和函數上下文。對於一個表達式語句,當你無法區分它是表達式還是語句, 你參照上下文,判斷程序在這需要做什麽,是變量賦值?還是語句執行?看到前面由“=”(賦值)或者用“()”(小括號,在這時分組符,裏面只能包含表達 式),你就可以肯定,這是一個表達式上下文。javascript的解釋器就是這個幹的。

var foo = function _foo(index){ 
    console.log("傳入的參數是:"+index||"空值")
}(1);  //輸出:傳入的參數是:1
console.log(foo);  //輸出:undefined
//根據上下文判斷,"var foo = "後面是表達式上下文,所有解釋器自動將後面看成一個立即執行函數,雖然沒有小括號。


function foo(index){ 
    console.log("傳入的參數是:"+index||"空值")
}(1)    //輸出:1
console.log(foo); //function foo(index){...}
//解釋器判斷此為語句上下文,所以講語句拆分為兩段。前一段為函數聲明,後一段“(1)”為一個簡單的分組語句。

  看到上面,你是不是靈機一動,發現你居然可以強制將表達式語句轉換成表達式了!

  轉換方法:

  (1)利用小括號,也就是分組符號(分組符內只允許表達式),進行轉換。

var o;
o = eval("{obj:‘this is a object‘}")
console.log(o); //this is a object

o = eval("({obj:‘this is a object‘})")
console.log(o); //Object {obj: "this is a object"}

前者eval中沒有加小括號,運行時被認為是一條語句執行,所以o被賦值成義字符串了;而後者,被認為是加上小括號,被認為是在表達式上下文中執行,所有返回一個對象。

  (2)利用運算符,因為javascript引擎會認為這是參與運算的必須是表達式。

+function(index){ 
    console.log("傳入的參數是:"+index||"空值");
     return index;
}(1)
//輸出:傳入的參數是:1
//表達式結果是:1

  記得上面我也運行過這個句代碼,不過當時前面少了個加號(“+”)。比較一下兩者的結果。

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