運(yùn)算符

運(yùn)算符是處理數(shù)據(jù)的基本方法,用來從現(xiàn)有的值得到新的值。JavaScript 提供了多種運(yùn)算符,本章逐一介紹這些運(yùn)算符。

加法運(yùn)算符

基本規(guī)則

加法運(yùn)算符(+)是最常見的運(yùn)算符,用來求兩個(gè)數(shù)值的和。

1 + 1 // 2

JavaScript 允許非數(shù)值的相加。

true + true // 2
1 + true // 2

上面代碼中,第一行是兩個(gè)布爾值相加,第二行是數(shù)值與布爾值相加。這兩種情況,布爾值都會(huì)自動(dòng)轉(zhuǎn)成數(shù)值,然后再相加。

比較特殊的是,如果是兩個(gè)字符串相加,這時(shí)加法運(yùn)算符會(huì)變成連接運(yùn)算符,返回一個(gè)新的字符串,將兩個(gè)原字符串連接在一起。

'a' + 'bc' // "abc"

如果一個(gè)運(yùn)算子是字符串,另一個(gè)運(yùn)算子是非字符串,這時(shí)非字符串會(huì)轉(zhuǎn)成字符串,再連接在一起。

1 + 'a' // "1a"
false + 'a' // "falsea"

加法運(yùn)算符是在運(yùn)行時(shí)決定,到底是執(zhí)行相加,還是執(zhí)行連接。也就是說,運(yùn)算子的不同,導(dǎo)致了不同的語(yǔ)法行為,這種現(xiàn)象稱為“重載”(overload)。由于加法運(yùn)算符存在重載,可能執(zhí)行兩種運(yùn)算,使用的時(shí)候必須很小心。

'3' + 4 + 5 // "345"
3 + 4 + '5' // "75"

上面代碼中,由于從左到右的運(yùn)算次序,字符串的位置不同會(huì)導(dǎo)致不同的結(jié)果。

除了加法運(yùn)算符,其他算術(shù)運(yùn)算符(比如減法、除法和乘法)都不會(huì)發(fā)生重載。它們的規(guī)則是:所有運(yùn)算子一律轉(zhuǎn)為數(shù)值,再進(jìn)行相應(yīng)的數(shù)學(xué)運(yùn)算。

1 - '2' // -1
1 * '2' // 2
1 / '2' // 0.5

上面代碼中,減法、除法和乘法運(yùn)算符,都是將字符串自動(dòng)轉(zhuǎn)為數(shù)值,然后再運(yùn)算。

對(duì)象的相加

如果運(yùn)算子是對(duì)象,必須先轉(zhuǎn)成原始類型的值,然后再相加。

var obj = { p: 1 };
obj + 2 // "[object Object]2"

上面代碼中,對(duì)象obj轉(zhuǎn)成原始類型的值是[object Object],再加2就得到了上面的結(jié)果。

對(duì)象轉(zhuǎn)成原始類型的值,規(guī)則如下。

首先,自動(dòng)調(diào)用對(duì)象的valueOf方法。

var obj = { p: 1 };
obj.valueOf() // { p: 1 }

一般來說,對(duì)象的valueOf方法總是返回對(duì)象自身,這時(shí)再自動(dòng)調(diào)用對(duì)象的toString方法,將其轉(zhuǎn)為字符串。

var obj = { p: 1 };
obj.valueOf().toString() // "[object Object]"

對(duì)象的toString方法默認(rèn)返回[object Object],所以就得到了最前面那個(gè)例子的結(jié)果。

知道了這個(gè)規(guī)則以后,就可以自己定義valueOf方法或toString方法,得到想要的結(jié)果。

var obj = {
  valueOf: function () {
    return 1;
  }
};

obj + 2 // 3

上面代碼中,我們定義obj對(duì)象的valueOf方法返回1,于是obj + 2就得到了3。這個(gè)例子中,由于valueOf方法直接返回一個(gè)原始類型的值,所以不再調(diào)用toString方法。

下面是自定義toString方法的例子。

var obj = {
  toString: function () {
    return 'hello';
  }
};

obj + 2 // "hello2"

上面代碼中,對(duì)象objtoString方法返回字符串hello。前面說過,只要有一個(gè)運(yùn)算子是字符串,加法運(yùn)算符就變成連接運(yùn)算符,返回連接后的字符串。

這里有一個(gè)特例,如果運(yùn)算子是一個(gè)Date對(duì)象的實(shí)例,那么會(huì)優(yōu)先執(zhí)行toString方法。

var obj = new Date();
obj.valueOf = function () { return 1 };
obj.toString = function () { return 'hello' };

obj + 2 // "hello2"

上面代碼中,對(duì)象obj是一個(gè)Date對(duì)象的實(shí)例,并且自定義了valueOf方法和toString方法,結(jié)果toString方法優(yōu)先執(zhí)行。

算術(shù)運(yùn)算符

包括加法運(yùn)算符在內(nèi),JavaScript 共提供10個(gè)算術(shù)運(yùn)算符,用來完成基本的算術(shù)運(yùn)算。

  • 加法運(yùn)算符x + y
  • 減法運(yùn)算符x - y
  • 乘法運(yùn)算符x * y
  • 除法運(yùn)算符x / y
  • 指數(shù)運(yùn)算符x ** y
  • 余數(shù)運(yùn)算符x % y
  • 自增運(yùn)算符++x 或者 x++
  • 自減運(yùn)算符--x 或者 x--
  • 數(shù)值運(yùn)算符+x
  • 負(fù)數(shù)值運(yùn)算符-x

減法、乘法、除法運(yùn)算法比較單純,就是執(zhí)行相應(yīng)的數(shù)學(xué)運(yùn)算。下面介紹其他幾個(gè)算術(shù)運(yùn)算符。

余數(shù)運(yùn)算符

余數(shù)運(yùn)算符(%)返回前一個(gè)運(yùn)算子被后一個(gè)運(yùn)算子除,所得的余數(shù)。

12 % 5 // 2

需要注意的是,運(yùn)算結(jié)果的正負(fù)號(hào)由第一個(gè)運(yùn)算子的正負(fù)號(hào)決定。

-1 % 2 // -1
1 % -2 // 1

所以,為了得到負(fù)數(shù)的正確余數(shù)值,可以先使用絕對(duì)值函數(shù)。

// 錯(cuò)誤的寫法
function isOdd(n) {
  return n % 2 === 1;
}
isOdd(-5) // false
isOdd(-4) // false

// 正確的寫法
function isOdd(n) {
  return Math.abs(n % 2) === 1;
}
isOdd(-5) // true
isOdd(-4) // false

余數(shù)運(yùn)算符還可以用于浮點(diǎn)數(shù)的運(yùn)算。但是,由于浮點(diǎn)數(shù)不是精確的值,無法得到完全準(zhǔn)確的結(jié)果。

6.5 % 2.1
// 0.19999999999999973

自增和自減運(yùn)算符

自增和自減運(yùn)算符,是一元運(yùn)算符,只需要一個(gè)運(yùn)算子。它們的作用是將運(yùn)算子首先轉(zhuǎn)為數(shù)值,然后加上1或者減去1。它們會(huì)修改原始變量。

var x = 1;
++x // 2
x // 2

--x // 1
x // 1

上面代碼的變量x自增后,返回2,再進(jìn)行自減,返回1。這兩種情況都會(huì)使得,原始變量x的值發(fā)生改變。

自增和自減運(yùn)算符有一個(gè)需要注意的地方,就是放在變量之后,會(huì)先返回變量操作前的值,再進(jìn)行自增/自減操作;放在變量之前,會(huì)先進(jìn)行自增/自減操作,再返回變量操作后的值。

var x = 1;
var y = 1;

x++ // 1
++y // 2

上面代碼中,x是先返回當(dāng)前值,然后自增,所以得到1y是先自增,然后返回新的值,所以得到2

數(shù)值運(yùn)算符,負(fù)數(shù)值運(yùn)算符

數(shù)值運(yùn)算符(+)同樣使用加號(hào),但它是一元運(yùn)算符(只需要一個(gè)操作數(shù)),而加法運(yùn)算符是二元運(yùn)算符(需要兩個(gè)操作數(shù))。

數(shù)值運(yùn)算符的作用在于可以將任何值轉(zhuǎn)為數(shù)值(與Number函數(shù)的作用相同)。

+true // 1
+[] // 0
+{} // NaN

上面代碼表示,非數(shù)值經(jīng)過數(shù)值運(yùn)算符以后,都變成了數(shù)值(最后一行NaN也是數(shù)值)。具體的類型轉(zhuǎn)換規(guī)則,參見《數(shù)據(jù)類型轉(zhuǎn)換》一章。

負(fù)數(shù)值運(yùn)算符(-),也同樣具有將一個(gè)值轉(zhuǎn)為數(shù)值的功能,只不過得到的值正負(fù)相反。連用兩個(gè)負(fù)數(shù)值運(yùn)算符,等同于數(shù)值運(yùn)算符。

var x = 1;
-x // -1
-(-x) // 1

上面代碼最后一行的圓括號(hào)不可少,否則會(huì)變成自減運(yùn)算符。

數(shù)值運(yùn)算符號(hào)和負(fù)數(shù)值運(yùn)算符,都會(huì)返回一個(gè)新的值,而不會(huì)改變?cè)甲兞康闹怠?/p>

指數(shù)運(yùn)算符

指數(shù)運(yùn)算符(**)完成指數(shù)運(yùn)算,前一個(gè)運(yùn)算子是底數(shù),后一個(gè)運(yùn)算子是指數(shù)。

2 ** 4 // 16

賦值運(yùn)算符

賦值運(yùn)算符(Assignment Operators)用于給變量賦值。

最常見的賦值運(yùn)算符,當(dāng)然就是等號(hào)(=)。

// 將 1 賦值給變量 x
var x = 1;

// 將變量 y 的值賦值給變量 x
var x = y;

賦值運(yùn)算符還可以與其他運(yùn)算符結(jié)合,形成變體。下面是與算術(shù)運(yùn)算符的結(jié)合。

// 等同于 x = x + y
x += y

// 等同于 x = x - y
x -= y

// 等同于 x = x * y
x *= y

// 等同于 x = x / y
x /= y

// 等同于 x = x % y
x %= y

// 等同于 x = x ** y
x **= y

下面是與位運(yùn)算符的結(jié)合(關(guān)于位運(yùn)算符,請(qǐng)見后文的介紹)。

// 等同于 x = x >> y
x >>= y

// 等同于 x = x << y
x <<= y

// 等同于 x = x >>> y
x >>>= y

// 等同于 x = x & y
x &= y

// 等同于 x = x | y
x |= y

// 等同于 x = x ^ y
x ^= y

這些復(fù)合的賦值運(yùn)算符,都是先進(jìn)行指定運(yùn)算,然后將得到值返回給左邊的變量。

比較運(yùn)算符

比較運(yùn)算符用于比較兩個(gè)值的大小,然后返回一個(gè)布爾值,表示是否滿足指定的條件。

2 > 1 // true

上面代碼比較2是否大于1,返回true

注意,比較運(yùn)算符可以比較各種類型的值,不僅僅是數(shù)值。

JavaScript 一共提供了8個(gè)比較運(yùn)算符。

  • < 小于運(yùn)算符
  • > 大于運(yùn)算符
  • <= 小于或等于運(yùn)算符
  • >= 大于或等于運(yùn)算符
  • == 相等運(yùn)算符
  • === 嚴(yán)格相等運(yùn)算符
  • != 不相等運(yùn)算符
  • !== 嚴(yán)格不相等運(yùn)算符

這八個(gè)比較運(yùn)算符分成兩類:相等比較和非相等比較。兩者的規(guī)則是不一樣的,對(duì)于非相等的比較,算法是先看兩個(gè)運(yùn)算子是否都是字符串,如果是的,就按照字典順序比較(實(shí)際上是比較 Unicode 碼點(diǎn));否則,將兩個(gè)運(yùn)算子都轉(zhuǎn)成數(shù)值,再比較數(shù)值的大小。

字符串的比較

字符串按照字典順序進(jìn)行比較。

'cat' > 'dog' // false
'cat' > 'catalog' // false

JavaScript 引擎內(nèi)部首先比較首字符的 Unicode 碼點(diǎn)。如果相等,再比較第二個(gè)字符的 Unicode 碼點(diǎn),以此類推。

'cat' > 'Cat' // true'

上面代碼中,小寫的c的 Unicode 碼點(diǎn)(99)大于大寫的C的 Unicode 碼點(diǎn)(67),所以返回true

由于所有字符都有 Unicode 碼點(diǎn),因此漢字也可以比較。

'大' > '小' // false

上面代碼中,“大”的 Unicode 碼點(diǎn)是22823,“小”是23567,因此返回false

非字符串的比較

(1)原始類型的值

兩個(gè)原始類型的值的比較,除了相等運(yùn)算符(==)和嚴(yán)格相等運(yùn)算符(===),其他比較運(yùn)算符都是先轉(zhuǎn)成數(shù)值再比較。

5 > '4' // true
// 等同于 5 > Number('4')
// 即 5 > 4

true > false // true
// 等同于 Number(true) > Number(false)
// 即 1 > 0

2 > true // true
// 等同于 2 > Number(true)
// 即 2 > 1

上面代碼中,字符串和布爾值都會(huì)先轉(zhuǎn)成數(shù)值,再進(jìn)行比較。

這里有一個(gè)特殊情況,即任何值(包括NaN本身)與NaN比較,返回的都是false

1 > NaN // false
1 <= NaN // false
'1' > NaN // false
'1' <= NaN // false
NaN > NaN // false
NaN <= NaN // false

(2)對(duì)象

如果運(yùn)算子是對(duì)象,會(huì)轉(zhuǎn)為原始類型的值,再進(jìn)行比較。

對(duì)象轉(zhuǎn)換成原始類型的值,算法是先調(diào)用valueOf方法;如果返回的還是對(duì)象,再接著調(diào)用toString方法,詳細(xì)解釋參見《數(shù)據(jù)類型的轉(zhuǎn)換》一章。

var x = [2];
x > '11' // true
// 等同于 [2].valueOf().toString() > '11'
// 即 '2' > '11'

x.valueOf = function () { return '1' };
x > '11' // false
// 等同于 [2].valueOf() > '11'
// 即 '1' > '11'

兩個(gè)對(duì)象之間的比較也是如此。

[2] > [1] // true
// 等同于 [2].valueOf().toString() > [1].valueOf().toString()
// 即 '2' > '1'

[2] > [11] // true
// 等同于 [2].valueOf().toString() > [11].valueOf().toString()
// 即 '2' > '11'

{x: 2} >= {x: 1} // true
// 等同于 {x: 2}.valueOf().toString() >= {x: 1}.valueOf().toString()
// 即 '[object Object]' >= '[object Object]'

注意,Date 對(duì)象實(shí)例用于比較時(shí),是先調(diào)用toString方法。如果返回的不是原始類型的值,再接著對(duì)返回值調(diào)用valueOf方法。

嚴(yán)格相等運(yùn)算符

JavaScript 提供兩種相等運(yùn)算符:=====

簡(jiǎn)單說,它們的區(qū)別是相等運(yùn)算符(==)比較兩個(gè)值是否相等,嚴(yán)格相等運(yùn)算符(===)比較它們是否為“同一個(gè)值”。如果兩個(gè)值不是同一類型,嚴(yán)格相等運(yùn)算符(===)直接返回false,而相等運(yùn)算符(==)會(huì)將它們轉(zhuǎn)換成同一個(gè)類型,再用嚴(yán)格相等運(yùn)算符進(jìn)行比較。

嚴(yán)格相等運(yùn)算符的算法如下。

(1)不同類型的值

如果兩個(gè)值的類型不同,直接返回false

1 === "1" // false
true === "true" // false

上面代碼比較數(shù)值的1與字符串的“1”、布爾值的true與字符串"true",因?yàn)轭愋筒煌Y(jié)果都是false

(2)同一類的原始類型值

同一類型的原始類型的值(數(shù)值、字符串、布爾值)比較時(shí),值相同就返回true,值不同就返回false

1 === 0x1 // true

上面代碼比較十進(jìn)制的1與十六進(jìn)制的1,因?yàn)轭愋秃椭刀枷嗤祷?code>true。

需要注意的是,NaN與任何值都不相等(包括自身)。另外,正0等于負(fù)0

NaN === NaN  // false
+0 === -0 // true

(3)復(fù)合類型值

兩個(gè)復(fù)合類型(對(duì)象、數(shù)組、函數(shù))的數(shù)據(jù)比較時(shí),不是比較它們的值是否相等,而是比較它們是否指向同一個(gè)地址。

{} === {} // false
[] === [] // false
(function () {} === function () {}) // false

上面代碼分別比較兩個(gè)空對(duì)象、兩個(gè)空數(shù)組、兩個(gè)空函數(shù),結(jié)果都是不相等。原因是對(duì)于復(fù)合類型的值,嚴(yán)格相等運(yùn)算比較的是,它們是否引用同一個(gè)內(nèi)存地址,而運(yùn)算符兩邊的空對(duì)象、空數(shù)組、空函數(shù)的值,都存放在不同的內(nèi)存地址,結(jié)果當(dāng)然是false

如果兩個(gè)變量引用同一個(gè)對(duì)象,則它們相等。

var v1 = {};
var v2 = v1;
v1 === v2 // true

注意,對(duì)于兩個(gè)對(duì)象的比較,嚴(yán)格相等運(yùn)算符比較的是地址,而大于或小于運(yùn)算符比較的是值。

new Date() > new Date() // false
new Date() < new Date() // false
new Date() === new Date() // false

上面的三個(gè)表達(dá)式,前兩個(gè)比較的是值,最后一個(gè)比較的是地址,所以都返回false

(4)undefined 和 null

undefinednull與自身嚴(yán)格相等。

undefined === undefined // true
null === null // true

由于變量聲明后默認(rèn)值是undefined,因此兩個(gè)只聲明未賦值的變量是相等的。

var v1;
var v2;
v1 === v2 // true

(5)嚴(yán)格不相等運(yùn)算符

嚴(yán)格相等運(yùn)算符有一個(gè)對(duì)應(yīng)的“嚴(yán)格不相等運(yùn)算符”(!==),它的算法就是先求嚴(yán)格相等運(yùn)算符的結(jié)果,然后返回相反值。

1 !== '1' // true

相等運(yùn)算符

相等運(yùn)算符用來比較相同類型的數(shù)據(jù)時(shí),與嚴(yán)格相等運(yùn)算符完全一樣。

比較不同類型的數(shù)據(jù)時(shí),相等運(yùn)算符會(huì)先將數(shù)據(jù)進(jìn)行類型轉(zhuǎn)換,然后再用嚴(yán)格相等運(yùn)算符比較。類型轉(zhuǎn)換規(guī)則如下。

(1)原始類型的值

原始類型的數(shù)據(jù)會(huì)轉(zhuǎn)換成數(shù)值類型再進(jìn)行比較。

1 == true // true
// 等同于 1 === Number(true)

0 == false // true
// 等同于 0 === Number(false)

2 == true // false
// 等同于 2 === Number(true)

2 == false // false
// 等同于 2 === Number(false)

'true' == true // false
// 等同于 Number('true') === Number(true)
// 等同于 NaN === 1

'' == 0 // true
// 等同于 Number('') === 0
// 等同于 0 === 0

'' == false  // true
// 等同于 Number('') === Number(false)
// 等同于 0 === 0

'1' == true  // true
// 等同于 Number('1') === Number(true)
// 等同于 1 === 1

'\n  123  \t' == 123 // true
// 因?yàn)樽址D(zhuǎn)為數(shù)字時(shí),省略前置和后置的空格

上面代碼將字符串和布爾值都轉(zhuǎn)為數(shù)值,然后再進(jìn)行比較。具體的字符串與布爾值的類型轉(zhuǎn)換規(guī)則,參見《數(shù)據(jù)類型轉(zhuǎn)換》一章。

(2)對(duì)象與原始類型值比較

對(duì)象(這里指廣義的對(duì)象,包括數(shù)組和函數(shù))與原始類型的值比較時(shí),對(duì)象轉(zhuǎn)化成原始類型的值,再進(jìn)行比較。

[1] == 1 // true
// 等同于 Number([1]) == 1

[1] == '1' // true
// 等同于 String([1]) == Number('1')

[1] == true // true
// 等同于 Number([1]) == Number(true)

上面代碼中,數(shù)組[1]與數(shù)值進(jìn)行比較,會(huì)先轉(zhuǎn)成數(shù)值,再進(jìn)行比較;與字符串進(jìn)行比較,會(huì)先轉(zhuǎn)成字符串,再進(jìn)行比較;與布爾值進(jìn)行比較,兩個(gè)運(yùn)算子都會(huì)先轉(zhuǎn)成數(shù)值,然后再進(jìn)行比較。

(3)undefined 和 null

undefinednull與其他類型的值比較時(shí),結(jié)果都為false,它們互相比較時(shí)結(jié)果為true

false == null // false
false == undefined // false

0 == null // false
0 == undefined // false

undefined == null // true

絕大多數(shù)情況下,對(duì)象與undefinednull比較,都返回false。只有在對(duì)象轉(zhuǎn)為原始值得到undefined時(shí),才會(huì)返回true,這種情況是非常罕見的。

(4)相等運(yùn)算符的缺點(diǎn)

相等運(yùn)算符隱藏的類型轉(zhuǎn)換,會(huì)帶來一些違反直覺的結(jié)果。

0 == ''             // true
0 == '0'            // true

2 == true           // false
2 == false          // false

false == 'false'    // false
false == '0'        // true

false == undefined  // false
false == null       // false
null == undefined   // true

' \t\r\n ' == 0     // true

上面這些表達(dá)式都很容易出錯(cuò),因此不要使用相等運(yùn)算符(==),最好只使用嚴(yán)格相等運(yùn)算符(===)。

(5)不相等運(yùn)算符

相等運(yùn)算符有一個(gè)對(duì)應(yīng)的“不相等運(yùn)算符”(!=),兩者的運(yùn)算結(jié)果正好相反。

1 != '1' // false

布爾運(yùn)算符

布爾運(yùn)算符用于將表達(dá)式轉(zhuǎn)為布爾值,一共包含四個(gè)運(yùn)算符。

  • 取反運(yùn)算符:!
  • 且運(yùn)算符:&&
  • 或運(yùn)算符:||
  • 三元運(yùn)算符:?:

取反運(yùn)算符(!)

取反運(yùn)算符是一個(gè)感嘆號(hào),用于將布爾值變?yōu)橄喾粗担?code>true變成falsefalse變成true

!true // false
!false // true

對(duì)于非布爾值,取反運(yùn)算符會(huì)將其轉(zhuǎn)為布爾值。可以這樣記憶,以下六個(gè)值取反后為true,其他值都為false

  • undefined
  • null
  • false
  • 0
  • NaN
  • 空字符串(''
!undefined // true
!null // true
!0 // true
!NaN // true
!"" // true

!54 // false
!'hello' // false
![] // false
!{} // false

上面代碼中,不管什么類型的值,經(jīng)過取反運(yùn)算后,都變成了布爾值。

如果對(duì)一個(gè)值連續(xù)做兩次取反運(yùn)算,等于將其轉(zhuǎn)為對(duì)應(yīng)的布爾值,與Boolean函數(shù)的作用相同。這是一種常用的類型轉(zhuǎn)換的寫法。

!!x
// 等同于
Boolean(x)

上面代碼中,不管x是什么類型的值,經(jīng)過兩次取反運(yùn)算后,變成了與Boolean函數(shù)結(jié)果相同的布爾值。所以,兩次取反就是將一個(gè)值轉(zhuǎn)為布爾值的簡(jiǎn)便寫法。

且運(yùn)算符(&&)

且運(yùn)算符(&&)往往用于多個(gè)表達(dá)式的求值。

它的運(yùn)算規(guī)則是:如果第一個(gè)運(yùn)算子的布爾值為true,則返回第二個(gè)運(yùn)算子的值(注意是值,不是布爾值);如果第一個(gè)運(yùn)算子的布爾值為false,則直接返回第一個(gè)運(yùn)算子的值,且不再對(duì)第二個(gè)運(yùn)算子求值。

't' && '' // ""
't' && 'f' // "f"
't' && (1 + 2) // 3
'' && 'f' // ""
'' && '' // ""

var x = 1;
(1 - 1) && ( x += 1) // 0
x // 1

上面代碼的最后一個(gè)例子,由于且運(yùn)算符的第一個(gè)運(yùn)算子的布爾值為false,則直接返回它的值0,而不再對(duì)第二個(gè)運(yùn)算子求值,所以變量x的值沒變。

這種跳過第二個(gè)運(yùn)算子的機(jī)制,被稱為“短路”。有些程序員喜歡用它取代if結(jié)構(gòu),比如下面是一段if結(jié)構(gòu)的代碼,就可以用且運(yùn)算符改寫。

if (i) {
  doSomething();
}

// 等價(jià)于

i && doSomething();

上面代碼的兩種寫法是等價(jià)的,但是后一種不容易看出目的,也不容易除錯(cuò),建議謹(jǐn)慎使用。

且運(yùn)算符可以多個(gè)連用,這時(shí)返回第一個(gè)布爾值為false的表達(dá)式的值。

true && 'foo' && '' && 4 && 'foo' && true
// ''

上面代碼中,第一個(gè)布爾值為false的表達(dá)式為第三個(gè)表達(dá)式,所以得到一個(gè)空字符串。

或運(yùn)算符(||)

且運(yùn)算符(||)也就是用于多個(gè)表達(dá)式的求值。

它的運(yùn)算規(guī)則是:如果第一個(gè)運(yùn)算子的布爾值為true,則返回第一個(gè)運(yùn)算子的值,且不再對(duì)第二個(gè)運(yùn)算子求值;如果第一個(gè)運(yùn)算子的布爾值為false,則返回第二個(gè)運(yùn)算子的值。

't' || '' // "t"
't' || 'f' // "t"
'' || 'f' // "f"
'' || '' // ""

短路規(guī)則對(duì)這個(gè)運(yùn)算符也適用。

var x = 1;
true || (x = 2) // true
x // 1

上面代碼中,且運(yùn)算符的第一個(gè)運(yùn)算子為true,所以直接返回true,不再運(yùn)行第二個(gè)運(yùn)算子。所以,x的值沒有改變。這種只通過第一個(gè)表達(dá)式的值,控制是否運(yùn)行第二個(gè)表達(dá)式的機(jī)制,就稱為“短路”(short-cut)。

或運(yùn)算符可以多個(gè)連用,這時(shí)返回第一個(gè)布爾值為true的表達(dá)式的值。

false || 0 || '' || 4 || 'foo' || true
// 4

上面代碼中第一個(gè)布爾值為true的表達(dá)式是第四個(gè)表達(dá)式,所以得到數(shù)值4。

或運(yùn)算符常用于為一個(gè)變量設(shè)置默認(rèn)值。

function saveText(text) {
  text = text || '';
  // ...
}

// 或者寫成
saveText(this.text || '')

上面代碼表示,如果函數(shù)調(diào)用時(shí),沒有提供參數(shù),則該參數(shù)默認(rèn)設(shè)置為空字符串。

三元條件運(yùn)算符(?:)

三元條件運(yùn)算符由問號(hào)(?)和冒號(hào)(:)組成,分隔三個(gè)表達(dá)式。它是 JavaScript 語(yǔ)言唯一一個(gè)需要三個(gè)運(yùn)算子的運(yùn)算符。如果第一個(gè)表達(dá)式的布爾值為true,則返回第二個(gè)表達(dá)式的值,否則返回第三個(gè)表達(dá)式的值。

't' ? 'hello' : 'world' // "hello"
0 ? 'hello' : 'world' // "world"

上面代碼的t0的布爾值分別為truefalse,所以分別返回第二個(gè)和第三個(gè)表達(dá)式的值。

通常來說,三元條件表達(dá)式與if...else語(yǔ)句具有同樣表達(dá)效果,前者可以表達(dá)的,后者也能表達(dá)。但是兩者具有一個(gè)重大差別,if...else是語(yǔ)句,沒有返回值;三元條件表達(dá)式是表達(dá)式,具有返回值。所以,在需要返回值的場(chǎng)合,只能使用三元條件表達(dá)式,而不能使用if..else

console.log(true ? 'T' : 'F');

上面代碼中,console.log方法的參數(shù)必須是一個(gè)表達(dá)式,這時(shí)就只能使用三元條件表達(dá)式。如果要用if...else語(yǔ)句,就必須改變整個(gè)代碼寫法了。

位運(yùn)算符

概述

位運(yùn)算符用于直接對(duì)二進(jìn)制位進(jìn)行計(jì)算,一共有7個(gè)。

  • 二進(jìn)制或運(yùn)算符(or):符號(hào)為|,表示若兩個(gè)二進(jìn)制位都為0,則結(jié)果為0,否則為1
  • 二進(jìn)制與運(yùn)算符(and):符號(hào)為&,表示若兩個(gè)二進(jìn)制位都為1,則結(jié)果為1,否則為0。
  • 二進(jìn)制否運(yùn)算符(not):符號(hào)為~,表示對(duì)一個(gè)二進(jìn)制位取反。
  • 異或運(yùn)算符(xor):符號(hào)為^,表示若兩個(gè)二進(jìn)制位不相同,則結(jié)果為1,否則為0。
  • 左移運(yùn)算符(left shift):符號(hào)為<<,詳見下文解釋。
  • 右移運(yùn)算符(right shift):符號(hào)為>>,詳見下文解釋。
  • 帶符號(hào)位的右移運(yùn)算符(zero filled right shift):符號(hào)為>>>,詳見下文解釋。

這些位運(yùn)算符直接處理每一個(gè)比特位(bit),所以是非常底層的運(yùn)算,好處是速度極快,缺點(diǎn)是很不直觀,許多場(chǎng)合不能使用它們,否則會(huì)使代碼難以理解和查錯(cuò)。

有一點(diǎn)需要特別注意,位運(yùn)算符只對(duì)整數(shù)起作用,如果一個(gè)運(yùn)算子不是整數(shù),會(huì)自動(dòng)轉(zhuǎn)為整數(shù)后再執(zhí)行。另外,雖然在 JavaScript 內(nèi)部,數(shù)值都是以64位浮點(diǎn)數(shù)的形式儲(chǔ)存,但是做位運(yùn)算的時(shí)候,是以32位帶符號(hào)的整數(shù)進(jìn)行運(yùn)算的,并且返回值也是一個(gè)32位帶符號(hào)的整數(shù)。

i = i | 0;

上面這行代碼的意思,就是將i(不管是整數(shù)或小數(shù))轉(zhuǎn)為32位整數(shù)。

利用這個(gè)特性,可以寫出一個(gè)函數(shù),將任意數(shù)值轉(zhuǎn)為32位整數(shù)。

function toInt32(x) {
  return x | 0;
}

上面這個(gè)函數(shù)將任意值與0進(jìn)行一次或運(yùn)算,這個(gè)位運(yùn)算會(huì)自動(dòng)將一個(gè)值轉(zhuǎn)為32位整數(shù)。下面是這個(gè)函數(shù)的用法。

toInt32(1.001) // 1
toInt32(1.999) // 1
toInt32(1) // 1
toInt32(-1) // -1
toInt32(Math.pow(2, 32) + 1) // 1
toInt32(Math.pow(2, 32) - 1) // -1

上面代碼中,toInt32可以將小數(shù)轉(zhuǎn)為整數(shù)。對(duì)于一般的整數(shù),返回值不會(huì)有任何變化。對(duì)于大于2的32次方的整數(shù),大于32位的數(shù)位都會(huì)被舍去。

二進(jìn)制或運(yùn)算符

二進(jìn)制或運(yùn)算符(|)逐位比較兩個(gè)運(yùn)算子,兩個(gè)二進(jìn)制位之中只要有一個(gè)為1,就返回1,否則返回0

0 | 3 // 3

上面代碼中,03的二進(jìn)制形式分別是0011,所以進(jìn)行二進(jìn)制或運(yùn)算會(huì)得到11(即3)。

位運(yùn)算只對(duì)整數(shù)有效,遇到小數(shù)時(shí),會(huì)將小數(shù)部分舍去,只保留整數(shù)部分。所以,將一個(gè)小數(shù)與0進(jìn)行二進(jìn)制或運(yùn)算,等同于對(duì)該數(shù)去除小數(shù)部分,即取整數(shù)位。

2.9 | 0 // 2
-2.9 | 0 // -2

需要注意的是,這種取整方法不適用超過32位整數(shù)最大值2147483647的數(shù)。

2147483649.4 | 0;
// -2147483647

二進(jìn)制與運(yùn)算符

二進(jìn)制與運(yùn)算符(|)的規(guī)則是逐位比較兩個(gè)運(yùn)算子,兩個(gè)二進(jìn)制位之中只要有一個(gè)位為0,就返回0,否則返回1

0 & 3 // 0

上面代碼中,0(二進(jìn)制00)和3(二進(jìn)制11)進(jìn)行二進(jìn)制與運(yùn)算會(huì)得到00(即0)。

二進(jìn)制否運(yùn)算符

二進(jìn)制否運(yùn)算符(~)將每個(gè)二進(jìn)制位都變?yōu)橄喾粗担?code>0變?yōu)?code>1,1變?yōu)?code>0)。它的返回結(jié)果有時(shí)比較難理解,因?yàn)樯婕暗接?jì)算機(jī)內(nèi)部的數(shù)值表示機(jī)制。

~ 3 // -4

上面表達(dá)式對(duì)3進(jìn)行二進(jìn)制否運(yùn)算,得到-4。之所以會(huì)有這樣的結(jié)果,是因?yàn)槲贿\(yùn)算時(shí),JavaScirpt 內(nèi)部將所有的運(yùn)算子都轉(zhuǎn)為32位的二進(jìn)制整數(shù)再進(jìn)行運(yùn)算。

3的32位整數(shù)形式是00000000000000000000000000000011,二進(jìn)制否運(yùn)算以后得到11111111111111111111111111111100。由于第一位(符號(hào)位)是1,所以這個(gè)數(shù)是一個(gè)負(fù)數(shù)。JavaScript 內(nèi)部采用補(bǔ)碼形式表示負(fù)數(shù),即需要將這個(gè)數(shù)減去1,再取一次反,然后加上負(fù)號(hào),才能得到這個(gè)負(fù)數(shù)對(duì)應(yīng)的10進(jìn)制值。這個(gè)數(shù)減去1等于11111111111111111111111111111011,再取一次反得到00000000000000000000000000000100,再加上負(fù)號(hào)就是-4。考慮到這樣的過程比較麻煩,可以簡(jiǎn)單記憶成,一個(gè)數(shù)與自身的取反值相加,等于-1。

~ -3 // 2

上面表達(dá)式可以這樣算,-3的取反值等于-1減去-3,結(jié)果為2

對(duì)一個(gè)整數(shù)連續(xù)兩次二進(jìn)制否運(yùn)算,得到它自身。

~~3 // 3

所有的位運(yùn)算都只對(duì)整數(shù)有效。二進(jìn)制否運(yùn)算遇到小數(shù)時(shí),也會(huì)將小數(shù)部分舍去,只保留整數(shù)部分。所以,對(duì)一個(gè)小數(shù)連續(xù)進(jìn)行兩次二進(jìn)制否運(yùn)算,能達(dá)到取整效果。

~~2.9 // 2
~~47.11 // 47
~~1.9999 // 1
~~3 // 3

使用二進(jìn)制否運(yùn)算取整,是所有取整方法中最快的一種。

對(duì)字符串進(jìn)行二進(jìn)制否運(yùn)算,JavaScript 引擎會(huì)先調(diào)用Number函數(shù),將字符串轉(zhuǎn)為數(shù)值。

// 相當(dāng)于~Number('011')
~'011'  // -12

// 相當(dāng)于~Number('42 cats')
~'42 cats' // -1

// 相當(dāng)于~Number('0xcafebabe')
~'0xcafebabe' // 889275713

// 相當(dāng)于~Number('deadbeef')
~'deadbeef' // -1

Number函數(shù)將字符串轉(zhuǎn)為數(shù)值的規(guī)則,參見《數(shù)據(jù)的類型轉(zhuǎn)換》一章。

對(duì)于其他類型的值,二進(jìn)制否運(yùn)算也是先用Number轉(zhuǎn)為數(shù)值,然后再進(jìn)行處理。

// 相當(dāng)于 ~Number([])
~[] // -1

// 相當(dāng)于 ~Number(NaN)
~NaN // -1

// 相當(dāng)于 ~Number(null)
~null // -1

異或運(yùn)算

異或運(yùn)算(^)在兩個(gè)二進(jìn)制位不同時(shí)返回1,相同時(shí)返回0

0 ^ 3 // 3

上面表達(dá)式中,0(二進(jìn)制00)與3(二進(jìn)制11)進(jìn)行異或運(yùn)算,它們每一個(gè)二進(jìn)制位都不同,所以得到11(即3)。

“異或運(yùn)算”有一個(gè)特殊運(yùn)用,連續(xù)對(duì)兩個(gè)數(shù)ab進(jìn)行三次異或運(yùn)算,a^=b; b^=a; a^=b;,可以互換它們的值。這意味著,使用“異或運(yùn)算”可以在不引入臨時(shí)變量的前提下,互換兩個(gè)變量的值。

var a = 10;
var b = 99;

a ^= b, b ^= a, a ^= b;

a // 99
b // 10

這是互換兩個(gè)變量的值的最快方法。

異或運(yùn)算也可以用來取整。

12.9 ^ 0 // 12

左移運(yùn)算符

左移運(yùn)算符(<<)表示將一個(gè)數(shù)的二進(jìn)制值向左移動(dòng)指定的位數(shù),尾部補(bǔ)0,即乘以2的指定次方(最高位即符號(hào)位不參與移動(dòng))。

// 4 的二進(jìn)制形式為100,
// 左移一位為1000(即十進(jìn)制的8)
// 相當(dāng)于乘以2的1次方
4 << 1
// 8

-4 << 1
// -8

上面代碼中,-4左移一位得到-8,是因?yàn)?code>-4的二進(jìn)制形式是11111111111111111111111111111100,左移一位后得到11111111111111111111111111111000,該數(shù)轉(zhuǎn)為十進(jìn)制(減去1后取反,再加上負(fù)號(hào))即為-8

如果左移0位,就相當(dāng)于將該數(shù)值轉(zhuǎn)為32位整數(shù),等同于取整,對(duì)于正數(shù)和負(fù)數(shù)都有效。

13.5 << 0
// 13

-13.5 << 0
// -13

左移運(yùn)算符用于二進(jìn)制數(shù)值非常方便。

var color = {r: 186, g: 218, b: 85};

// RGB to HEX
// (1 << 24)的作用為保證結(jié)果是6位數(shù)
var rgb2hex = function(r, g, b) {
  return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
    .toString(16) // 先轉(zhuǎn)成十六進(jìn)制,然后返回字符串
    .substr(1);   // 去除字符串的最高位,返回后面六個(gè)字符串
}

rgb2hex(color.r, color.g, color.b)
// "#bada55"

上面代碼使用左移運(yùn)算符,將顏色的 RGB 值轉(zhuǎn)為 HEX 值。

右移運(yùn)算符

右移運(yùn)算符(>>)表示將一個(gè)數(shù)的二進(jìn)制值向右移動(dòng)指定的位數(shù),頭部補(bǔ)0,即除以2的指定次方(最高位即符號(hào)位不參與移動(dòng))。

4 >> 1
// 2
/*
// 因?yàn)?的二進(jìn)制形式為 00000000000000000000000000000100,
// 右移一位得到 00000000000000000000000000000010,
// 即為十進(jìn)制的2
*/

-4 >> 1
// -2
/*
// 因?yàn)?4的二進(jìn)制形式為 11111111111111111111111111111100,
// 右移一位,頭部補(bǔ)1,得到 11111111111111111111111111111110,
// 即為十進(jìn)制的-2
*/

右移運(yùn)算可以模擬 2 的整除運(yùn)算。

5 >> 1
// 2
// 相當(dāng)于 5 / 2 = 2

21 >> 2
// 5
// 相當(dāng)于 21 / 4 = 5

21 >> 3
// 2
// 相當(dāng)于 21 / 8 = 2

21 >> 4
// 1
// 相當(dāng)于 21 / 16 = 1

帶符號(hào)位的右移運(yùn)算符

帶符號(hào)位的右移運(yùn)算符(>>>)表示將一個(gè)數(shù)的二進(jìn)制形式向右移動(dòng),包括符號(hào)位也參與移動(dòng),頭部補(bǔ)0。所以,該運(yùn)算總是得到正值。對(duì)于正數(shù),該運(yùn)算的結(jié)果與右移運(yùn)算符(>>)完全一致,區(qū)別主要在于負(fù)數(shù)。

4 >>> 1
// 2

-4 >>> 1
// 2147483646
/*
// 因?yàn)?4的二進(jìn)制形式為11111111111111111111111111111100,
// 帶符號(hào)位的右移一位,得到01111111111111111111111111111110,
// 即為十進(jìn)制的2147483646。
*/

這個(gè)運(yùn)算實(shí)際上將一個(gè)值轉(zhuǎn)為32位無符號(hào)整數(shù)。

查看一個(gè)負(fù)整數(shù)在計(jì)算機(jī)內(nèi)部的儲(chǔ)存形式,最快的方法就是使用這個(gè)運(yùn)算符。

-1 >>> 0 // 4294967295

上面代碼表示,-1作為32位整數(shù)時(shí),內(nèi)部的儲(chǔ)存形式使用無符號(hào)整數(shù)格式解讀,值為 4294967295(即(2^32)-1,等于11111111111111111111111111111111)。

開關(guān)作用

位運(yùn)算符可以用作設(shè)置對(duì)象屬性的開關(guān)。

假定某個(gè)對(duì)象有四個(gè)開關(guān),每個(gè)開關(guān)都是一個(gè)變量。那么,可以設(shè)置一個(gè)四位的二進(jìn)制數(shù),它的每個(gè)位對(duì)應(yīng)一個(gè)開關(guān)。

var FLAG_A = 1; // 0001
var FLAG_B = 2; // 0010
var FLAG_C = 4; // 0100
var FLAG_D = 8; // 1000

上面代碼設(shè)置 A、B、C、D 四個(gè)開關(guān),每個(gè)開關(guān)分別占有一個(gè)二進(jìn)制位。

然后,就可以用二進(jìn)制與運(yùn)算檢驗(yàn),當(dāng)前設(shè)置是否打開了指定開關(guān)。

var flags = 5; // 二進(jìn)制的0101

if (flags & FLAG_C) {
  // ...
}
// 0101 & 0100 => 0100 => true

上面代碼檢驗(yàn)是否打開了開關(guān)C。如果打開,會(huì)返回true,否則返回false

現(xiàn)在假設(shè)需要打開ABD三個(gè)開關(guān),我們可以構(gòu)造一個(gè)掩碼變量。

var mask = FLAG_A | FLAG_B | FLAG_D;
// 0001 | 0010 | 1000 => 1011

上面代碼對(duì)ABD三個(gè)變量進(jìn)行二進(jìn)制或運(yùn)算,得到掩碼值為二進(jìn)制的1011

有了掩碼,二進(jìn)制或運(yùn)算可以確保打開指定的開關(guān)。

flags = flags | mask;

二進(jìn)制與運(yùn)算可以將當(dāng)前設(shè)置中凡是與開關(guān)設(shè)置不一樣的項(xiàng),全部關(guān)閉。

flags = flags & mask;

異或運(yùn)算可以切換(toggle)當(dāng)前設(shè)置,即第一次執(zhí)行可以得到當(dāng)前設(shè)置的相反值,再執(zhí)行一次又得到原來的值。

flags = flags ^ mask;

二進(jìn)制否運(yùn)算可以翻轉(zhuǎn)當(dāng)前設(shè)置,即原設(shè)置為0,運(yùn)算后變?yōu)?code>1;原設(shè)置為1,運(yùn)算后變?yōu)?code>0。

flags = ~flags;

其他運(yùn)算符

void 運(yùn)算符

void運(yùn)算符的作用是執(zhí)行一個(gè)表達(dá)式,然后不返回任何值,或者說返回undefined

void 0 // undefined
void(0) // undefined

上面是void運(yùn)算符的兩種寫法,都正確。建議采用后一種形式,即總是使用圓括號(hào)。因?yàn)?code>void運(yùn)算符的優(yōu)先性很高,如果不使用括號(hào),容易造成錯(cuò)誤的結(jié)果。比如,void 4 + 7實(shí)際上等同于(void 4) + 7

下面是void運(yùn)算符的一個(gè)例子。

var x = 3;
void (x = 5) //undefined
x // 5

這個(gè)運(yùn)算符的主要用途是瀏覽器的書簽工具(bookmarklet),以及在超級(jí)鏈接中插入代碼防止網(wǎng)頁(yè)跳轉(zhuǎn)。

請(qǐng)看下面的代碼。

<script>
function f() {
  console.log('Hello World');
}
</script>
<a  onclick="f(); return false;">點(diǎn)擊</a>

上面代碼中,點(diǎn)擊鏈接后,會(huì)先執(zhí)行onclick的代碼,由于onclick返回false,所以瀏覽器不會(huì)跳轉(zhuǎn)到 example.com

void運(yùn)算符可以取代上面的寫法。

<a href="javascript: void(f())">文字</a>

下面是一個(gè)更實(shí)際的例子,用戶點(diǎn)擊鏈接提交表單,但是不產(chǎn)生頁(yè)面跳轉(zhuǎn)。

<a href="javascript: void(document.form.submit())">
  提交
</a>

逗號(hào)運(yùn)算符

逗號(hào)運(yùn)算符用于對(duì)兩個(gè)表達(dá)式求值,并返回后一個(gè)表達(dá)式的值。

'a', 'b' // "b"

var x = 0;
var y = (x++, 10);
x // 1
y // 10

上面代碼中,逗號(hào)運(yùn)算符返回后一個(gè)表達(dá)式的值。

運(yùn)算順序

優(yōu)先級(jí)

JavaScript 各種運(yùn)算符的優(yōu)先級(jí)別(Operator Precedence)是不一樣的。優(yōu)先級(jí)高的運(yùn)算符先執(zhí)行,優(yōu)先級(jí)低的運(yùn)算符后執(zhí)行。

4 + 5 * 6 // 34

上面的代碼中,乘法運(yùn)算符(*)的優(yōu)先性高于加法運(yùn)算符(+),所以先執(zhí)行乘法,再執(zhí)行加法,相當(dāng)于下面這樣。

4 + (5 * 6) // 34

如果多個(gè)運(yùn)算符混寫在一起,常常會(huì)導(dǎo)致令人困惑的代碼。

var x = 1;
var arr = [];

var y = arr.length <= 0 || arr[0] === undefined ? x : arr[0];

上面代碼中,變量y的值就很難看出來,因?yàn)檫@個(gè)表達(dá)式涉及5個(gè)運(yùn)算符,到底誰(shuí)的優(yōu)先級(jí)最高,實(shí)在不容易記住。

根據(jù)語(yǔ)言規(guī)格,這五個(gè)運(yùn)算符的優(yōu)先級(jí)從高到低依次為:小于等于(<=)、嚴(yán)格相等(===)、或(||)、三元(?:)、等號(hào)(=)。因此上面的表達(dá)式,實(shí)際的運(yùn)算順序如下。

var y = ((arr.length <= 0) || (arr[0] === undefined)) ? x : arr[0];

記住所有運(yùn)算符的優(yōu)先級(jí),是非常難的,也是沒有必要的。

圓括號(hào)的作用

圓括號(hào)(())可以用來提高運(yùn)算的優(yōu)先級(jí),因?yàn)樗膬?yōu)先級(jí)是最高的,即圓括號(hào)中的表達(dá)式會(huì)第一個(gè)運(yùn)算。

(4 + 5) * 6 // 54

上面代碼中,由于使用了圓括號(hào),加法會(huì)先于乘法執(zhí)行。

運(yùn)算符的優(yōu)先級(jí)別十分繁雜,且都是硬性規(guī)定,因此建議總是使用圓括號(hào),保證運(yùn)算順序清晰可讀,這對(duì)代碼的維護(hù)和除錯(cuò)至關(guān)重要。

順便說一下,圓括號(hào)不是運(yùn)算符,而是一種語(yǔ)法結(jié)構(gòu)。它一共有兩種用法:一種是把表達(dá)式放在圓括號(hào)之中,提升運(yùn)算的優(yōu)先級(jí);另一種是跟在函數(shù)的后面,作用是調(diào)用函數(shù)。

注意,因?yàn)閳A括號(hào)不是運(yùn)算符,所以不具有求值作用,只改變運(yùn)算的優(yōu)先級(jí)。

var x = 1;
(x) = 2;

上面代碼的第二行,如果圓括號(hào)具有求值作用,那么就會(huì)變成1 = 2,這是會(huì)報(bào)錯(cuò)了。但是,上面的代碼可以運(yùn)行,這驗(yàn)證了圓括號(hào)只改變優(yōu)先級(jí),不會(huì)求值。

這也意味著,如果整個(gè)表達(dá)式都放在圓括號(hào)之中,那么不會(huì)有任何效果。

(exprssion)
// 等同于
expression

函數(shù)放在圓括號(hào)中,會(huì)返回函數(shù)本身。如果圓括號(hào)緊跟在函數(shù)的后面,就表示調(diào)用函數(shù)。

function f() {
  return 1;
}

(f) // function f(){return 1;}
f() // 1

上面代碼中,函數(shù)放在圓括號(hào)之中會(huì)返回函數(shù)本身,圓括號(hào)跟在函數(shù)后面則是調(diào)用函數(shù)。

圓括號(hào)之中,只能放置表達(dá)式,如果將語(yǔ)句放在圓括號(hào)之中,就會(huì)報(bào)錯(cuò)。

(var a = 1)
// SyntaxError: Unexpected token var

左結(jié)合與右結(jié)合

對(duì)于優(yōu)先級(jí)別相同的運(yùn)算符,大多數(shù)情況,計(jì)算順序總是從左到右,這叫做運(yùn)算符的“左結(jié)合”(left-to-right associativity),即從左邊開始計(jì)算。

x + y + z

上面代碼先計(jì)算最左邊的xy的和,然后再計(jì)算與z的和。

但是少數(shù)運(yùn)算符的計(jì)算順序是從右到左,即從右邊開始計(jì)算,這叫做運(yùn)算符的“右結(jié)合”(right-to-left associativity)。其中,最主要的是賦值運(yùn)算符(=)和三元條件運(yùn)算符(?:)。

w = x = y = z;
q = a ? b : c ? d : e ? f : g;

上面代碼的運(yùn)算結(jié)果,相當(dāng)于下面的樣子。

w = (x = (y = z));
q = a ? b : (c ? d : (e ? f : g));

上面的兩行代碼,各有三個(gè)等號(hào)運(yùn)算符和三個(gè)三元運(yùn)算符,都是先計(jì)算最右邊的那個(gè)運(yùn)算符。

參考鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,654評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

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