表達式是javascript中的一個短語,javascript解釋器會將其計算出一個結果。程式中常用量是最簡單的一類表達式就是變數。變數名也是一種簡單的表達式,它的值就是賦值給變數的值。
複雜的表達式是由簡單的表達式組成的。例如數組訪問表達式是由一個表示數組的表達式,方括號、一個整數表達式構成。它們所組成新的表達式運算結果是該陣列特定位置的元素值。同樣的函
數呼叫表達式由一個表示函數物件的表達式和0個多個參數表達式構成。將簡單表達式組成複雜表達式最常用的方法就是運算子。
本章(本文)將講解所有javascript運算子。同時也講解不涉及運算子的表達式(例如存取數組元素和函數呼叫),其語法和程式設計風格和c語言都很相似。
1.元素表達式
最簡單的表達式是“原始表達式”,原始表達式是表達式的最小的單位--它們不包含其他表達式。 javascript中的原始表達式包含常數或直接量。關鍵字和變數。
直接量是直接在程式中出現的常數值。它們看起來像:
javascript中的一些保留字構成了原始表達式
透過第三章的學習,和其它關鍵字不同,this並不是一個常數,他在程式的不同地方返回的值也不相同。 this關鍵字經常在物件導向程式設計中出現。 this傳回方格方法的物件。
最後,第三種原始表達式是變數
2.物件和陣列的初始化表達式。
物件和陣列初始化其實是新建立的物件和數組,這些初始化的表達式有時候叫做「物件直接量」和「陣列直接量」。然而和布林直接量不同,他們不是原始表達式,因為他們所包含的成員或元素都子表達式。
陣列的初始化表達式語法非常簡單,我們以下開始
陣列的初始化表達式是透過一對方括號和其內由逗號隔開的列表構成的,初始化結果是一個新建立的陣列。數組的元素是逗號分隔表達式的值。
[] //一個空數組;[]內留空即表示該數組沒有任何元素
[1 2,3 4] //有兩個元素的數組,第一個3,第二個是7
數組初始化表達式中的元素初始化表達式可以是數組初始化表達式。也就是說表達式是可以嵌套的
數組直接量中列表之間的元素可以省略,空位就會填充undefined.例如下面:
其中4個元素是undefined.數組直接量的結尾處留下逗號,這時不會創建一個新的值為undefined的元素。
物件初始化表達式和陣列初始化表達式非常相似,只是方括號被花括號取代。並每個字表達式包含一個屬性名和非冒號作為前綴。
物件直接量也可以嵌套,例如
javascript在計算物件初始化表達式的值時候,物件表達式都會各自計算一次,而且不必包含常數值:它們可以是任意javascript表達式。同樣,物件直接量中屬性的名稱可以是字串而不是識別符。 (在在那行只能使用保留字或一些非法識別字作為屬性名的時候非常有用)
第6 7章也會再次討論物件和陣列的初始化表達式。
3.函數表達式
函數定義表達式定義一個javascript函數。表達式的值是這個新定義的函數。從某種意義上將,函數定義表達式可以成為函數直接量,函數表達式可稱為“函數直接量”,畢竟物件初始化表達式也稱為“物件直接量”。一個典型的函數定義表達式包含關鍵字function,其後是一對圓括號,括號以內是逗號分隔的列表,列表包含0或多個標識符(參數名)。然後跟著花括號包裹的javascript程式碼段(函數體).
var square = function(x){ return x*x};
函數定義表達式同樣可以包含函數的名字。函數也可以透過函數語句來定義,而不是函數表達式。更多內容會在第八章描述。
4.屬性存取表達式
屬性存取運算式運算得到一個物件或一個陣列元素的值。 javascript為屬性存取定義了兩種方法。
第一種寫法是一個表達式後跟隨一個句點和標識符。表達式指定對象,標識符則指定要存取的屬性明川。
第二章寫法是使用方括號,方括號內是一個表達式(這個方法適用於物件和陣列)。第二個表達式指定要存取的屬性的明川或代表要存取陣列元素的索引。這裡有一些具體的例子
不管使用哪一種形式的屬性存取表達式,在"."和"["之前的表達式總是會先計算。如果計算結果為null或undefined,則表達式會拋出類型錯誤異常,因為這兩個值都不能包含任意屬性。如果運算結果不是物件或數組,javascript會將其轉換為物件(3章6節內容)
雖然.identifier的寫法比較簡單,但要注意的是,這種方式只適用於要存取的屬性名稱是合法的識別碼。並且需要知道要存取的屬性名字。如果屬性名稱是保留字或包含空格和標點符號,是一個數字(對於陣列來說),則必須使用方括號的寫法。當屬性名稱是透過運算子得出的值而不是固定值的時候,這時候必須使用方括號的寫法。 (6章2節1小節)
5.調運表達式
javascript中的呼叫表達式(invocation expression)是一種呼叫(或執行)函數或方法的語法表示。它以一個函數表達式開始,這個函數表達式指了要呼叫的函數。函數表達式後跟著一對圓括號,括號內是一個以逗號隔開的參數列表。參數可以有0個也可以有多個。
f(0) //f是函數式:0是參數式。
Math.max(x,y,z) //Math.max是函數;x,y和z是參數
a.sort() //a.sort()是函數,它沒有參數。
當呼叫表達式進行求值的時候,先計算函數表達式,然後計算參數表達式,得到一組參數值。如果函數表達式的值不是一個可調用的對象,則拋出一個類型錯誤異常.然後參數的值依次被賦值給形參,這些形參是定義函數時定義的。接下來執行函數體。如果函數使用return語句給出一個回傳值,那麼這個回傳值就是整個呼叫表達式的值。否則,呼叫表達式的值就是undefined.函數呼叫--包括形參表達式的個數和函數定義中實參的個數不匹配的時候運行的情況--的細節將會在第八章詳細說明。
任何一個呼叫表達式都包含一對圓括號和左圓括號之前的表達式,如果這個表達式是一個屬性存取表達式,那麼這個呼叫叫做「方法呼叫」(method invication)。在方法呼叫中執行函數體的時候,作為屬性存取主體的物件和陣列便是其呼叫方法內this的指向。這種特性使得在物件導向程式設計的範例中,函數(其OO名稱為「方法」)可呼叫其宿主物件(第9章會有更多相關內容)。
6.物件建立表達式
物件建立表達式(object creation expression)建立一個物件並呼叫一個函數(建構函式)來初始化物件的屬性。物件建立表達式和函數呼叫表達式非常類似,只是物件建立表達式之前多了一個關鍵字new:
new Object()
new Point(2,3)
如果物件建立表達式不需要傳入任何參數給建構函式的話,那麼這對圓括號是可以省略掉的,更多建構函式的細節將在9章說明
new Object
new Point
7.運算子概述
javascript中的運算子用於算表表達式, 比較表達式, 邏輯表達式 ,賦值表達式等
要注意的是大多運算子都是標點符號來表示的,例如delete和instanceof.無論是關鍵字運算子還是符號運算符,所表示的運算子一樣都是正規運算符,他們的語法都非常言簡意賅。
下標運算子的優先權來排序的,前邊的運算子優先權高於後邊的運算子優先權。被水平華豐隔開的運算子具有不同的優先權。
A表示運算符的結合性。
L由左至右或R(由右至左)
標題N的列表表示操作數的個數。
類型表示期望的操作數的類型,以及運算符的結果類型(在"→"符號之後)
運算子 | 操作 | A | N | 類型 |
前/後增量 | R | 1 | lval→num | |
-- | 前後減量 | R | 1 | lval→num |
- | 求反 | R | 1 | num→num |
轉換為數字 | R | 1 | num→num | |
~ | 按位求反 | R | 1 | int→int |
! | 邏輯非 | R | 1 | bool→bool |
delete | 刪除屬性 | R | 1 | lval→bool |
typeof | 偵測操作類型 | R | 1 | any→Str |
void | 傳回undefined值 | R | 1 | any→undef |
* 、/、% | 乘 除 求餘 | L | 2 | num,num→num |
、- | 加,減 | L | 2 | num,num→num |
字串連線 | L | 2 | str,str→str | |
左移位 | L | 2 | int,int→int | |
>> | 右移位 | L | 2 | int,int→int |
>>> | 無符號右移 |
L |
2 | int,int→int |
,>= | 比較數字順序 | L | 2 | num,num→bool |
,>= | 比較在字母中的順序 | L | 2 | str,str→bool |
instanceof | 測試物件類別 | L | 2 | obj,func→bool |
in | 測試屬性是否存在 | L | 2 | str,obj→bool |
== | 判斷相等 | L | 2 | any,any→bool |
! = | 判斷不等 | L | 2 | any,any→bool |
=== | 判斷恆等 | L | 2 | any,any→bool |
! == | 判斷非恆等 | L | 2 | any,any→bool |
& | 位元與 | L | 2 | int,int→int |
^ | 位元異或 | L | 2 | int,int→int |
| | 位元或 | L | 2 | int,int→int |
&& | 邏輯與 | L | 2 | any,any→any |
|| | 邏輯或 | L | 2 | any,any→any |
?: | 條件運算子 | R | 3 | bool,any,any→any |
= | 變數賦值或物件屬性賦值 | R | 2 | lval,any→any |
*= /= %= = -= &= ^= |= >= >>>= |
運算且賦值 | R | 2 | lval,any→any |
, |
忽略第一個操作數, 傳回第二個操作數。 |
L | 2 | any,any→any |
i.操作數的個數
運算子可以透過運算元的個數來分類。
javascript中的大多數運算符是二元運算符,將兩個表達式合併成一個稍微複雜的表達式。
javascript也支援一些一元運算符,它們將一個表達式轉換為另一個稍微複雜的表達式。表達式-x中的"-"運算子就是一個一元運算子。是將x求負值。
javascript支援一個三元運算子:條件判斷運算子「?:」,它將三個表達式合併為一個表達式
ii.操作數型態與結果型態
一些運算符可以用於任何資料類型,但仍然希望它們操作指定的類型的資料。
iii.左值
在表中的賦值運算子和其它少數運算子期望它們的操作數lval類型,左值是一個古老的術語。它是指「表達式只能出現在賦值運算子的左邊」。 javascript中,變數、物件屬性和陣列元素都是左值。 ECMAScript規格允許範圍內建函數傳回一個左值,但定義的函數則不能傳回左值。
iiii.運算子的優先權
在上表中,所示的運算子是依照優先權從高到低排序的,每個水平分隔線內一組運算子有相同的優先權。運算子優先權優先控制著運算子的執行順序。運算子高的(表頂)的執行總是高於優先順序低的(表格的底部)運算子。
看如下表達式
w=x y*z;
乘法運算子「*」比加法「 」有更高的優先權,所以乘法先執行。然後,由於賦值運算子“=”具有最低優先權。因此賦值運算是在右側的表達式計算出結果後進行的。
運算子的優先權可以使用園括號來從寫。以上表達式可以這樣寫。
w = (x y) * z;
需要注意的是,屬性存取表達式和呼叫表達式的優先權要比表中的所有運算子都要高。
typeof my.Function[x](y)
儘管typeof是優先權最高的運算子之一,但typeof也是在兩次屬性存取和函數呼叫後執行的。
事實上,如果你真的不確定你所使用的運算子優先級,最簡單的方法就是使用園括號來強行指定運算次序。有些重要的規則則要熟記:乘法和除法高於加減法,賦值運算的優先權非常低,通常是最後執行的。
iiiiii.運算子的結合性
在本節表中,標題為A的列說明了運算子的結核性。 L指由左至右結合,R指由右至左結合。結核性指定了在多個具有相同優先權的運算子表達式中的運算順序。
例如,減法運算依照由左到右的執行結合性。
跟這段程式碼一樣:
反過來講,下面這個表達式:
跟這段程式碼一模一樣
因為一元運算子、賦值和三元條件運算子都具有從右到左的結合性。
iiiiiii.運算順序
運算子的優先順序和結合性規定了它們在賦值的運算式中的運算順序,但並沒有規定字表達式的計算過程中的運算順序。 javascript總是嚴格地按照從左到右的順序計算表達式,例如:
在表達式 w=x y*z 中,將先計算表達式w,然後計算x、y和z,然後,y的值和z相乘,在加上x的值。最後將其表達式w所指涉的變數或屬性。為表達式加入園括號將會改變乘法,加法和賦值運算的關係。但從左到右的順序是不會改變的。
8.算術表達式
本節涵蓋了那些算術計算的運算子、以及對運算元的算術運算。乘法、除法和減法運算子非常簡單。加法運算單獨一節,因為加法運算子可以操作字串的連接,且其型別轉換有些特殊。
基本的算術運算子是*、/、%、 、-。除了 加法,其它的運算子特別簡單,只是在必要的時候操作符轉化為數字而已,然後求積、商、餘(模)和差。所有那些無法轉換為數字的操作都將轉換為NaN值。如果操作數(或轉換結果)是NaN值,算術運算結果也是NaN
運算子「/」用第二個運算元來除以第一個運算元,如果你使用過那些區分整數型和浮點數型的程式語言。那麼用一個整數除以一個整數時,則希望得到的結果也是整數。在javascript中所有的數字都是浮點數型的,除法運算的結果也是浮點型。例如5/2結果是2.5,而不是2。除數為0的運算結果為正無窮大或負無窮大。而0/0的結果是NaN。所有這些運算均不會報錯。
運算子「%」計算的是第一個操作數對第二個操作數的模,換句話說,就是第一個操作數除以第二個操作鼠的餘數。結果的符號和第一個操作鼠(被除數)符號保持一致。例如5%2的結果為1,-5%2為-1。
求餘運算子的運算元通常都是整數,但也適用於浮點數。 6.5%2.1結果是0.2。 (0.19999999999999973)
i.「 」運算子
二元加法運算子「 」可以對兩個數字做加法,也可以做字串連接運算:
當兩個運算元都是數字或都是字串的時候,計算結果是顯而易見的。然而對於其他情況來說,則要進行一些必要的類型轉換。並且運算符的行為依賴於類型的轉換的結果。從技術上來講,加法操作符的行為表現為:
如果一個操作數是對象,則對象會遵循對像到原始值的轉換規則為原始類別值(參考3章8節3小節)。日期對物件toString()方法執行轉換,其他物件則透過valueOf()方法執行轉換(如果valueOf()方法傳回一個原始值的話)。由於多數物件都不具備可用的valueOf()方法,因此他們會透過toString()方法來執行抓換
在進行了物件到原始值的轉換後,如果其中一個操作鼠是字串的話,另一個操作數也會轉換為字串。然後進行字串連接。
否則,兩個操作數都會轉換為數字(或NaN),然後進行加法操作。
這裡有一些例子
最後,特別要注意的是。當加號運算符合字串一起使用時,要考慮加法對運算順序的影響。也就是說,運算結果是依賴運算子的運算順序的,例如
ii.一元運算子
一元運算子作用於一個單獨的運算元。並產生一個新值。在javascript中,一元運算子具有很高的優先級,而且都是右邊結合。本節講述一元運算子( ,-, 和--),必要時,他們將操作轉換為數字。需要注意的是 -是一元運算符,也是二元運算符、
一元加法
一元加法運算子把運算元轉換為數字(或NaN),且傳回這個轉換後的數字。如果運算元本身就是數字,則直接傳回這個數字。
一元減法-
當-號做一元運算子時,它會依照需求將運算元轉換為數字,然後改變運算結果的符號、
遞增
增加「 」運算子對其運算元進行增量( 1)的操作,而操作數一個左值(變數、陣列元素或物件屬性)。運算子將操作數轉換為數字。然後給數字加1、並將加1後的數值重新賦值給變數、陣列元素或物件屬性。
增加 運算回傳值依賴它對運算元的位置。
當運算子在操作數數之前,且稱為「前增量」(pre-increment)運算符,它對運算元進行增量計算,並傳回計算後的數值。
當運算子在操作數之後,稱為"後增量"(post-increment)運算符,它對操作數進行增量計算,但返回為做增量計算的(unincremented)值。如
var i = 1,j = i //i和j的數值為2
var i = 1,j = i ; //i是2,j是1
需要注意的是,便打算x並總和x=x 1完全一樣,“ ”運算符從不進行字串連接操作,它總會將操作數轉換為數字並增1.如果x是字串“1” , x的結果就是數字2,而x 1是字串"11"
遞減和遞增的操作方式是相同的,它把操作數轉換為數組,然後減1.
iii.位元運算子
位元運算子可以對數字表示的二進位資料進行較低層級的位元運算。儘管它們不是傳統的純數學運算,但這裡也歸類為算術運算符,因為他們作用於數值類型的操作並傳回數字。這些運算符在javascript不常見。 (此處不描述,詳情自行百度~-~)
9.關係式
本節講述javascript的關係運算符,關係運算子用於測試兩個值之間中的關係(相等、小於或「是...的屬性」),根據關係是否存在而傳回true和false .關係式運算式總是傳回一個布林值,通常在if while或是for語句(第五章)中使用關係式,以控製程式的執行流程。
接下來幾節會講述相等和不等運算、比較運算子和javascript中其它兩個關係符in和instanceof
i相等且不等運算子
「==」和"==="運算子用於比較兩個值是否相等,兩個運算子允許任意類型的運算子。如果相等則回傳true,否則傳回false.「===」也稱為嚴格相等運算子(有時稱為恆等運算子),它用於偵測兩個運算元是否嚴格相等。 「==」運算子稱為相等運算符,它用來偵測兩個運算元是否相等,這裡的相等定義寬鬆,可以允許進行型別轉換。
javascript支援“=”,“==”,“===”運算符,你應該理解(賦值,相等,恆等)運算符之間的區別。並在編程中小心使用。為了減少混淆,應該把“=”稱作“得到或賦值”,把“==”稱作“相等”,把“===”稱作“嚴格相等”。
“!=”和“!==”運算符規則是“==”,“===”運算符的求反,“!”是布爾非運算符,我們將“!=”,“ !==」稱為不相等,不嚴格相等
javascript物件的比較是引用的比較,而不是值的比較。物件和本身是相等的,但和人和物件都不相等。如果兩個物件具有相同數量的屬性,相同的屬性名稱和值,它們仍然是不相等的。對應位置的數組元素是相等的兩個數組也是不相等的。
嚴格相等運算子"==="先計算運算元的值,然後比較這兩個值,比較過程中沒有任何型別轉換。
如果兩個值型別不想同,則它們不相等
如果兩個值都是null或undefined,則它們不相等
如果兩個值都是布林值true或false, 則它們相等
如果其中一個值是NaN,或兩個值都是NaN ,則它們不相等,NaN和其它值都是不相等的,包括它本身。
如果兩個值為數字且相等,則它們相等。如果一個值為0,令一個值為-0,則它們同樣相等。
如果兩個值為字串,且所含對應位上的16位數(參考3章2節)完全相等,則它們相等。如果他們的長度或內容不同,則不相等。兩個字串可能函數完全一樣且所顯示的字元也一樣,但具有不用編碼的16位元值,javascript並不對Unicode進行標準轉換,因此這樣的字串透過"==="和"=="運算子的比較結果也不相等。第三部分的String.localeCompare()提供了另一個比較字串的方法。
如果兩個引用值指向同一個對象,數組或函數,則它們是相等的。如果指向不同的對象,則它們是不等的,儘管兩個對像有完全相同的屬性。
相等運算子"=="和恆等運算子相似,但相等運算子比較並不嚴格。如果兩個數字不是相同類型,那麼相等運算子會嘗試進行一些類型轉換,然後進行比較。
如果兩個運算型別相同,則和上文相等運算子的比較規則一樣。如果嚴格相等,那麼比較結果相等。如果他們不嚴格相等,則比較結果不相等。
如果兩個運算型別不同,「==」相等運算子也會認為它們相等。偵測相等會遵循如下的規則和型別轉換:
如果一個類型是null,令一個是undefined,則它們相等
如果一個值是數字,另一個是字串,先將字串轉換為數字,然後再使用轉換後的值來比較。
如果一個值是true,則將其轉換為1再進行比較,如果一個值為false,則轉換為0比較。
如果一個值是對象,另一個值是數字或字串,則使用3章8節3小節的方法的轉換規則將物件轉換為原始值,然後進行比較。物件透過toString()方法或valueOf()方法轉換為原始值。 javascript語言核心的內建類別首先嘗試使用valueOf()再嘗試使用toString(),除了日期類,日期類只能透過toString()轉換。那些不是javascript 語言核心中的物件則透過實作中定義的方法轉換為原始值。
其它不同類型之間的比較均不相等
這裡有一個判斷相等的小例子
"1" == true
這個表達式的結果為true,這表示完全不同類型的值比較結果為相等。布林值true先轉換為數字1 ,然後再執行比較。接下來字串「1」也會轉換為數字1 ,因為兩個數字的值相等,因此結果為true.
ii.比較運算子
小於(
如果第一個運算元小於第二個運算元,「
小於等於(
大於(>)
大於等於(>=)
....(不詳細介紹意義)
比較操作符的操作數可能是任意型別。然而只有數字和字串才能真正執行比較操作符,因此,那些不是數字和字串的操作數都會進行類型轉換。類型轉換規則如下:
如果操作數為對象,則依照3章8節3小節處鎖描述的轉換規則轉換為原始值:如果valueOf()傳回一個原始值,那麼就直接使用這個原始值。否則使用toString() 的轉換結果進行比較。
在對轉換為原始值之後,如果兩個運算元都是字串,那麼將依字母表的順序對兩個字串進行比較,這裡提到的「字母表順序」是組成這兩個字串的16位元Unicode字元的索引順序。
在物件轉換為原始值之後,如果至少一個操作數不去是字串,那麼兩個操作數都會為數字進行數值的比較。 0和-0是相等的。 Infinty壁其它任何數字都大(除了infinty本身),-infinty比任何數字都小(除了它自己本身。)如果一個操作數(或轉換後)為NaN,那麼比較符總是返回false
對於數字和字串運算子來說,加號運算子和比較運算子的行為有所不同 ,前者更偏愛字串,如果它的其中一個運算元是字串的話,則進行字串連接運算。而比較運算子則更偏好數字,只有在兩個運算元都是字串字串的時候。才會進行字串的比較。
最後要注意的是,「=」運算子在判斷相等的時候,並不依賴相等運算子和和嚴格相等運算比較規則。相反,小於等於運算符芝是簡單的“不大於”,大於等於運算只是“不小於”。只有一個例外,的那個一個操作數(後轉換後)是NaN的時候,所有4個比較運算子都會回傳fasle.
iii.in運算子
in運算子希望它的左操作數是一個字串或可以轉換為字串,希望它的右邊是一個物件。如果右側的物件擁有一個名為左操作數值的屬性名,那麼表達式傳回true.例如
var data = [7, 8, 8]
"0" in data //=>true 陣列包含0
1 in data //=>true 數位轉換為字串
3 in data //=>fase 沒有索引為3的元素
iiii.instanceof運算子
instanceof運算子希望左操作符為一個對象,右操作數標示對象的類別。如果左側的物件是右側類別的實例,則表達式傳回true;負責傳回false.第9章將會講到。 javascript物件的類別是透過初始化他們的建構函數的來定義的。這樣的話,instanceof的右邊操作數應是一個函數。如:
要注意的是,所有物件都是Object的實例。當透過instanceof盤對一個物件是否為一個類別的實例的時候,這個判斷也叫做「父類別」(superclass)的偵測,如果instanceof的左側操作物件不是物件的話,instanceof回傳false。如果右側操作不是函數,則拋出類型錯誤的異常。
為了理解instanceof運算子是如何運作的,必須先理解「原型類別」(prototype chain),原型鍊作為javascript的繼承機制,將在6章2節2小節詳細描述。
為了計算表達式o instanceof f ,javascript筆仙首先計算f.prototyoe,然後在原型鏈中查詢o,如果找到,那麼o是f(或者f的父類)的一個實例,那麼返回true。反之false
10.邏輯表達式
邏輯運算子"&&"、「||」、「!」是對運算進行布林算術運算,經常和關係運算子一起配合使用,邏輯運算子將多個關係表達式組合起來組成一個更複雜的表達式。
i.邏輯與
"&&"運算子可以從三個不同的層次來理解。最簡單一層理解是,當操作數都是布林值是,「&&」對兩個布林值執行布林與(AND)操作,只有在第一個操作數和第二個操作數都是true的時候,它才回傳true.如果其中有一個操作數為false.則它傳回false.
"&&"長用來連接兩個關係式
x == 0 && y == 0; //只有在x和y都是0時,才會回傳true
關係表達式總是回傳true或false,因此當這樣使用的時候,「&&」本身也回傳true或 false。關係運算子的優先順序要比"&&"(和「||」)高,因此類似這種表達式可以放心地書寫,而不用補充園括號。
"&&"運算元不一定是布林值,回想一下,有些值是可以當做「真值」和「假值」的。 (如3章3節,假值是:false null undefined 0 -0 NaN和"",所有和其它的值包括所有的物件都是真值)。對「&&」第二層理解是,「&&」是可以對真值和假值進行布林與(AND)操作。如果兩個運算元都是真值的,則那麼傳回一個真值;否則,至少一個運算元是假值的。 javascript在任何使用布林值的地方的時候,表達式語句都會將其當做真值或假值來對待,因此實際上「&&」並不總是返回true和false.但也並無大礙。
需要注意的是,上文提到了運算子返回“真值”和“假值”,但並沒說說明這個“真值”或“假值”到底是什麼值,為此我們深入討論對「&&」第三層的理解。運算子首先計算左運算元的值,也就是先計算「&&」左邊的表達式,如果計算結果是假值,那麼整個表達式的結果一定是假值,因此「&&」這時簡單的回傳左操作的值,而不會對右邊的操作數進行計算。
這對於理解「&&」可能不計算右運算元的情況至關重要,在上述程式碼中,變數P的值是null,而如果計算p.x的話則會拋出一個異常錯誤,因此,只有p為真值(不能是null或undefined)的情況下才計算p.x
"&&"的行為有時候被稱為「短路」(short circuiting),我們經常能看到很多程式碼利用了這一也行來有條件的執行程式碼。例如下面的兩個程式碼是等價的
一般來講,當「&&」右邊的表達式具有副作用的時候(賦值,遞增,遞減和函數呼叫表達式)要格外小心。因為當這些有副作用的表達式的執行時候,就依賴左操作鼠的計算結果。
儘管「&&」可以按照第二層和第三層的理解進行一些複雜的表達式運算,但大多數的情況下,「&&」只用來對真值和假值的做布林計算。
ii.邏輯或(||)
"||"運算子對兩個運算元做布林或(OR)運算。如果其中一個為真值,則傳回真值,兩個運算元都為假值,傳回假值。
儘管「||」運算子大多情況下只是做簡單的布林或(OR)運算,和「&&」一樣,也具備一些更複雜的行為,它首先計算第一個操作數的值,也是說回先計算左邊的表達式,如果計算結果為真,則回傳真值,否則,再計算第二個值。
和「&&」一樣,用於應該避免右操作數包含一些具有副作用的表達式,除非你目地明確在右側使用帶副作用的表達式,而有可能不會計算右側的表達式。
這個運算子最常用的方式是用來從一組備選的表達式中選取第一個真值的表達式。
這種貫用法通常在函數體內,用來給參數預設值。
iii.邏輯非(!)
"!"運算子是一元運算符,它被放置在一個單獨操作數之前。它的目的是將操作數的布林值求反。
和"&&"、"||"運算子不同,「!」運算子先將其運算元轉換為布林值(參考第三章的講訴規則),然後再對布林值求反。也就是"!"總是回傳true和 false。並且,可以透過使用兩次邏輯非運算來得到一個值的布林值:(!!x,參考第三章第八節第2小節)
「!」具有很高的優先級,並且和操作數緊密的綁在一起,如果希望對p && q,則需要園括號!(p && q)。如下碼:
對於p和q取任何值,這兩個表達式永遠成立。
11.賦值表達式
javascript使用"="運算子給變數或屬性來賦值,例如:
「=」運算子希望它的左操作數為一個左值:一個變數或物件屬性(或陣列元素),它的右操作鼠可以是任意的型別的任意值。賦值表達式的值就是右邊操作數的值。賦值表達式的副作用是,右操作數的值賦值給左側的變數或物件屬性。這樣的話,後續對這個變數和物件的屬性的引用都會得到這個值。
儘管賦值表達式的值非常簡單,但有時候會看到一些複雜表達式包含賦值表達式的情況。例如:將賦值和偵測運算放在一個表達式中:
如果這樣的話,應該要清楚知道"="和"=="差別! ,需要注意的是,「=」有非常低的優先級,通常在一個較長的表達式中用到一條賦值語句時,需要補充園括號以保障正確的運算順序。
賦值運算子的結合性是從右到左,也就是說,一個表達式中出現了多個賦值運算符,運算順序也從右到左,因此,可以透過以下方式對多個變數賦值。
帶運算的賦值運算:
除了常規的賦值運算外,javascript還支援需要其他的賦值運算符,這些運算符將賦值運算符合其他的運算子連接起來。提供一種更快速的運算方式。例如 =運算子執行的是加法運算子和賦值運算,下面的表達式:
total = salaes_tax;
和下面的表達式等價的
total = total salaes_tax
運算子「 =」可以作用於數字或字串,如果其操作是數字,它將執行加法運算和賦值操作;如果是字串,他就執行字串的連接和賦值操作。
此類型的運算子還包括,"-=","*=","&="等,如下表賦值運算子
運算子 範例 等價於
= a =b a=a b
-= a-=b a=a-b
*= a*=b a=a*b
/= a/=b a=a/b
%= a%=b a=a%b
>>= a>>=b a=a>>b
>>>= a>>>=b a=a>>>b
&= a&=b a=a&b
|= a|=b a=a|b
^= a^=b a=a^b
大多數情況下,表達式為
a op =b
這裡的op代表一個運算符,這個表達式等價於
a =a op b
在第一行中,表達式a計算了一次,在第二行中,表達式a計算了兩次。
只有 a包含具有副作用的表達式(例如函數呼叫和賦值運算)的時候,兩者才不等價。如下兩個表達式不等價
12.表達式計算
和許多解釋性語言一樣,javascript同樣可以解釋運行由javascript原始碼組成的字串,並產生一個值。 javascript透過全域函數eval()來完成這個工作。
eval("3 2") //=>5
動態判斷原始碼中的字串是一種強大語言的特性,幾乎沒有必要在實際中應用。如果你使用了eval(),你應該仔細考慮真的需要它。
下面降價eval()基礎用法,並介紹兩種嚴格使用它的方法,從程式碼最佳化的角度來講,這兩種方法對原有的程式碼影響是最小的。
i.eval(eval()是一個函數,但由於它已經被當做運算子來對待了。)
eval()只有一個參數,如果傳入的參數不是字串,它就直接回傳這個參數。如果參數是字串,它會把字串當成javascript進行編譯(parse),如果編譯失敗則拋出一個語法錯誤(SyntaxError)。如果編譯成功,則開始執行這段程式碼,並傳回字串中最後一個表達式或語句的值,如果最後一個表達式沒有語句或值,則最終傳回undefined。如果字串拋出一個異常,這個異常把該呼叫的傳給eval()
關於eveal()最重要的是,它使用了呼叫它的變數作用域環境,也就是說,它尋找變數的值和定義新變數和函數的操作和局部的程式碼作用域中的程式碼一樣。如果一個函數定義了一個局部變數x,然後呼叫了eval("x"),它會傳回局部變數的值。如果它呼叫eval("x=1"),它會改變局部變數的值。如果函數呼叫了eval("var y=3;")它宣告一個新的局部變數y。同樣的,一個函數可以透過以下程式碼宣告一個局部函數:
eval("function f(){return x 1;}");
如果最頂層的程式碼中呼叫了eval()。當然它會作用於全域變數和全域函數。
ii.全域eval()
eval()具有改變局部變數的能力,這對javascript優化器來說,是一個很大的問題,然而作為一種權宜之計,javascript徵對那行調用了eval()函數所做的優化並不多。但當腳本定義了一個別名,並且用令一個名稱來呼叫它,javascript解釋器又如何運作呢,為了javascript解釋器更加簡化。 ECMAScipt3標準規定了任何解釋器都不允許對eval()賦予別名。如果eval()使用別的別名來呼叫的話,則會拋出EvalError異常。
實際上,大多數的實作並不是這樣做的。當透過別名呼叫時,eval()會將其字串當成頂層的全域程式碼來執行。執行程式碼可能會定義新的全域變數和全域函數。執行的程式碼可能會定義新的全域變數和全域函數,或為全域變數賦值。但卻不能修改或修改主調函數中的局部變量,因此這不會影響到函數內的程式碼最佳化。
ECMAScript5是反對使用EvalError的,並且規範了eval()的行為。 “直接的eval”,當直接使用非限定的“eval”名稱,來呼叫eval()函數時,它總共是在它的上下文作用域內支線。其它間接呼叫則使用全域函數為其上下文作用域。且無法讀取、寫入、定義局部變數和函數。下面有一段程式碼實例:
JavaScript は他にもさまざまな演算子をサポートしています。
i. 条件演算子 (?:)条件演算子は、JavaScript の唯一の三項演算子です。通常、この演算子は「?:」として記述されます。最初のオペランドは「?」の前にあり、2 番目のオペランドは「?」と「:」の間にあります。 3 番目のオペランドは、
などの「:」の直後にあります。x > 0 ? x : -x; 条件演算子のオペランドは任意の型にすることができます。最初のオペランドがブール値として扱われる場合、2 番目のオペランドが評価され、結果が返されます。割り当て 最初の値のオペランドが false の場合、3 番目のオペランドが評価されます。そして計算結果を返します。 2 番目と 3 番目のオペランドは常にいずれかの値として評価されます。両方を同時に行うことは不可能です。実際、if ステートメント (5.4.1) を使用しても同じ効果が得られます。「?:」演算子は単に省略形を提供するだけです。以下は、変数が定義されているかどうかを決定する「?:」の一般的な使用シナリオです。定義されていない場合は、デフォルト値が使用されます。