咱們從學校裏瞭解到過不少運算符,好比說加號 +
、乘號 *
、減號 -
等。javascript
在這個章節,咱們將關注一些在學校數學課程中沒有涵蓋的運算符。html
在正式開始前,咱們先簡單瀏覽一下經常使用術語。java
運算元 —— 運算符應用的對象。好比說乘法運算 5 * 2
,有兩個運算元:左運算元 5
和右運算元 2
。有時候人們也稱其爲「參數」。react
若是一個運算符對應的只有一個運算元,那麼它是 一元運算符。好比說一元負號運算符(unary negation)-
,它的做用是對數字進行正負轉換:程序員
let x = 1;
x = -x;
alert( x ); // -1,一元負號運算符生效
複製代碼
若是一個運算符擁有兩個運算元,那麼它是 二元運算符。減號還存在二元運算符形式:編程
let x = 1, y = 3;
alert( y - x ); // 2,二元運算符減號作減運算
複製代碼
嚴格地說,在上面的示例中,咱們使用一個相同的符號表徵了兩個不一樣的運算符:負號運算符,即反轉符號的一元運算符,減法運算符,是從另外一個數減去一個數的二進制運算符。微信
下面,讓咱們看一下在學校數學課程範圍外的 JavaScript 運算符特性。app
一般,加號 +
用於求和。框架
可是若是加號 +
被應用於字符串,它將合併(鏈接)各個字符串:編程語言
let s = "my" + "string";
alert(s); // mystring
複製代碼
注意:只要其中一個運算元是字符串,那麼另外一個運算元也將被轉化爲字符串。
舉個例子:
alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"
複製代碼
能夠看出,字符串在前和在後並不影響這個規則。簡單來講:若是任一運算元是字符串,那麼其它運算元也將被轉化爲字符串。
可是,請注意:運算符的運算方向是由左至右。若是是兩個數字,後面再跟一個字符串,那麼兩個數字會先相加,再轉化爲字符串:
alert(2 + 2 + '1' ); // "41" 而不是 "221"
複製代碼
字符串鏈接和轉化是二元運算符加號 +
的一個特性。其它的數學運算符都只對數字有效。一般,他們會把運算元轉化爲數字。
舉個例子,減法和除法:
alert( 2 - '1' ); // 1
alert( '6' / '2' ); // 3
複製代碼
加號 +
有兩種形式。一種是上面咱們剛剛討論的二元運算符,還有一種是一元運算符。
一元運算符加號,或者說,加號 +
應用於單個值,對數字沒有任何做用。可是若是運算元不是數字,加號 +
則會將其轉化爲數字。
例如:
// 對數字無效
let x = 1;
alert( +x ); // 1
let y = -2;
alert( +y ); // -2
// 轉化非數字
alert( +true ); // 1
alert( +"" ); // 0
複製代碼
它的效果和 Number(...)
相同,可是更加簡短。
咱們常常會有將字符串轉化爲數字的需求。好比,若是咱們正在從 HTML 表單中取值,一般獲得的都是字符串。若是咱們想對他們求和,該怎麼辦?
二元運算符加號會把他們合併成字符串:
let apples = "2";
let oranges = "3";
alert( apples + oranges ); // "23",二元運算符加號合併字符串
複製代碼
若是咱們想把它們當作數字對待,咱們須要轉化它們,而後再求和:
let apples = "2";
let oranges = "3";
// 在二元運算符加號起做用以前,全部的值都被轉化爲了數字
alert( +apples + +oranges ); // 5
// 更長的寫法
// alert( Number(apples) + Number(oranges) ); // 5
複製代碼
從一個數學家的視角來看,大量的加號可能很奇怪。可是從一個程序員的視角,沒什麼好奇怪的:一元運算符加號首先起做用,他們將字符串轉爲數字,而後二元運算符加號對它們進行求和。
爲何一元運算符先於二元運算符做用於運算元?接下去咱們將討論到,這是因爲它們擁有 更高的優先級。
若是一個表達式擁有超過一個運算符,執行的順序則由 優先級 決定。換句話說,全部的運算符中都隱含着優先級順序。
從小學開始,咱們就知道在表達式 1 + 2 * 2
中,乘法先於加法計算。這就是一個優先級問題。乘法比加法擁有 更高的優先級。
圓括號擁有最高優先級,因此若是咱們對現有的運算順序不滿意,咱們可使用圓括號來修改運算順序,就像這樣:(1 + 2) * 2
。
在 JavaScript 中有衆多運算符。每一個運算符都有對應的優先級數字。數字越大,越先執行。若是優先級相同,則按照由左至右的順序執行。
這是一個摘抄自 Mozilla 的 優先級表(你沒有必要把這全記住,但要記住一元運算符優先級高於二元運算符):
優先級 | 名稱 | 符號 |
---|---|---|
... | ... | ... |
16 | 一元加號 | + |
16 | 一元負號 | - |
14 | 乘號 | * |
14 | 除號 | / |
13 | 加號 | + |
13 | 減號 | - |
... | ... | ... |
3 | 賦值符 | = |
... | ... | ... |
咱們能夠看到,「一元運算符加號」的優先級是 16
,高於「二元運算符加號」的優先級 13
。這也是爲何表達式 "+apples + +oranges"
中的一元加號先生效,而後纔是二元加法。
咱們知道賦值符號 =
也是一個運算符。從優先級表中能夠看到它的優先級很是低,只有 3
。
這也是爲何,當咱們賦值時,好比 x = 2 * 2 + 1
,全部的計算先執行,而後 =
才執行,將計算結果存儲到 x
。
let x = 2 * 2 + 1;
alert( x ); // 5
複製代碼
鏈式賦值也是能夠的:
let a, b, c;
a = b = c = 2 + 2;
alert( a ); // 4
alert( b ); // 4
alert( c ); // 4
複製代碼
鏈式賦值由右向左執行。首先執行最右側表達式 2 + 2
,而後將結果賦值給左側:c
、b
、a
。最後,全部的變量都共享一個值。
賦值運算符
\"=\"
會返回一個值每一個運算符都有一個返回值。對於以加號
+
或者乘號*
爲例的大部分運算符而言,這一點很顯然。對於賦值運算符而言,這一點一樣適用。語句
x = value
把value
的值寫入x
而後返回 x。下面是一個在複雜語句中使用賦值的例子:
let a = 1; let b = 2; let c = 3 - (a = b + 1); alert( a ); // 3 alert( c ); // 0 複製代碼
上面這個例子,
(a = b + 1)
的結果是賦給a
的值(也就是3
)。而後該值被用於進一步的運算。這段代碼是否是很好玩兒?咱們應該理解它的原理,由於咱們有時會在第三方庫中見到這樣的寫法,但咱們本身不該該這樣寫。這樣的小技巧讓代碼變得整潔度和可讀性都不好。
求餘運算符 %
儘管看上去是個百分號,但它和百分數沒有什麼關係。
a % b
的結果是 a
除以 b
的餘數。
舉個例子:
alert( 5 % 2 ); // 1 是 5 / 2 的餘數
alert( 8 % 3 ); // 2 是 8 / 3 的餘數
alert( 6 % 3 ); // 0 是 6 / 3 的餘數
複製代碼
冪運算符 **
是最近被加入到 JavaScript 中的。
對於天然數 b
,a ** b
的結果是 a
與本身相乘 b
次。
舉個例子:
alert( 2 ** 2 ); // 4 (2 * 2)
alert( 2 ** 3 ); // 8 (2 * 2 * 2)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
複製代碼
這個運算符對於 a
和 b
是非整數的狀況依然適用。
例如:
alert( 4 ** (1/2) ); // 2 (1/2 冪至關於開方,這是數學常識)
alert( 8 ** (1/3) ); // 2 (1/3 冪至關於開三次方)
複製代碼
對一個數進行加1、減一是最多見的數學運算符之一。
因此,對此有一些專門的運算符:
自加 ++
將變量與 1 相加:
let counter = 2;
counter++; // 和 counter = counter + 1 效果同樣,可是更簡潔
alert( counter ); // 3
複製代碼
自減 --
將變量與 1 相減:
let counter = 2;
counter--; // 和 counter = counter - 1 效果同樣,可是更簡潔
alert( counter ); // 1
複製代碼
注意:
自加/自減只能應用於變量。試一下,將其應用於數值(好比
5++
)則會報錯。
運算符 ++
和 --
能夠置於變量前,也能夠置於變量後。
counter++
。++counter
。二者都作同一件事:將變量 counter
與 1
相加。
那麼他們有區別嗎?有,但只有當咱們使用 ++/--
的返回值時才能看到區別。
詳細點說。咱們知道,全部的運算符都有返回值。自加/自減也不例外。前置形式返回一個新的值,但後置返回原來的值(作加法/減法以前的值)。
爲了直觀看到區別,看下面的例子:
let counter = 1;
let a = ++counter; // (*)
alert(a); // 2
複製代碼
(*)
所在的行是前置形式 ++counter
,對 counter
作自加運算,返回的是新的值 2
。所以 alert
顯示的是 2
。
下面讓咱們看看後置形式:
let counter = 1;
let a = counter++; // (*) 將 ++counter 改成 counter++
alert(a); // 1
複製代碼
(*)
所在的行是後置形式 counter++
,它一樣對 counter
作加法,可是返回的是 舊值(作加法以前的值)。所以 alert
顯示的是 1
。
總結:
若是自加/自減的值不會被使用,那麼二者形式沒有區別:
let counter = 0;
counter++;
++counter;
alert( counter ); // 2,以上兩行做用相同
複製代碼
若是咱們想要對變量進行自加操做,而且 須要馬上使用自加後的值,那麼咱們須要使用前置形式:
let counter = 0;
alert( ++counter ); // 1
複製代碼
若是咱們想要將一個數加一,可是咱們想使用其自加以前的值,那麼咱們須要使用後置形式:
let counter = 0;
alert( counter++ ); // 0
複製代碼
自加/自減和其它運算符的對比
++/--
運算符一樣能夠在表達式內部使用。它們的優先級比絕大部分的算數運算符要高。舉個例子:
let counter = 1; alert( 2 * ++counter ); // 4 複製代碼
與下方例子對比:
let counter = 1; alert( 2 * counter++ ); // 2,由於 counter++ 返回的是「舊值」 複製代碼
儘管從技術層面上來講可行,可是這樣的寫法會下降代碼的可閱讀性。在一行上作多個操做 —— 這樣並很差。
當閱讀代碼時,快速的視覺「縱向」掃描會很容易漏掉
counter++
,這樣的自增操做並不明顯。咱們建議「一行一個操做」模式:
let counter = 1; alert( 2 * counter ); counter++; 複製代碼
位運算符把運算元當作 32 位整數,並在它們的二進制表現形式上操做。
這些運算符不是 JavaScript 特有的。大部分的編程語言都支持這些運算符。
下面是位運算符:
&
)|
)^
)~
)<<
)>>
)>>>
)這些操做使用得很是少。爲了理解它們,咱們須要探討底層的數字表達形式,如今不是作這個的最好時機。尤爲是咱們如今不會馬上使用它。若是你感興趣,能夠閱讀 MDN 中的 位運算符 相關文章。當有相關實際需求的時候再去閱讀是更明智的選擇。
咱們常常須要對一個變量進行操做,並把計算獲得的新結果存儲在這個變量中。
舉個例子:
let n = 2;
n = n + 5;
n = n * 2;
複製代碼
這個操做能夠經過使用運算符 +=
和 *=
進行簡化:
let n = 2;
n += 5; // now n = 7 (同 n = n + 5)
n *= 2; // now n = 14 (同n = n * 2)
alert( n ); // 14
複製代碼
簡短的「修改並替換」 運算符對全部的運算符包括位運算符都有效:/=
、-=
等等。
這些運算符和正常的賦值運算符擁有相同的優先級,所以它們會在其它大部分運算完成以後運行:
let n = 2;
n *= 3 + 5;
alert( n ); // 16(右側計算首先進行,和 n *= 8 相同)
複製代碼
逗號運算符 ,
是最少見最不常使用的運算符之一。有時候它會被用來寫更簡短的代碼,所以爲了可以理解代碼,咱們須要瞭解它。
逗號運算符能讓咱們處理多個語句,使用 ,
將它們分開。每一個語句都運行了,可是隻有最後的語句的結果會被返回。
舉個例子:
let a = (1 + 2, 3 + 4);
alert( a ); // 7(3 + 4 的結果)
複製代碼
這裏,第一個語句 1 + 2
運行了,可是它的結果被丟棄了。隨後計算 3 + 4
,而且該計算結果被返回。
逗號運算符的優先級很是低
請注意逗號運算符的優先級很是低,比
=
還要低,所以上面你的例子中圓括號很是重要。若是沒有圓括號:
a = 1 + 2, 3 + 4
會先執行+
,將數值相加獲得a = 3, 7
,而後賦值運算符=
執行, 'a = 3',而後逗號以後的數值7
不會再執行,它被忽略掉了。至關於(a = 1 + 2), 3 + 4
。
爲何咱們須要這樣一個運算符,它只返回最後一個值呢?
有時候,人們會使用它把幾個操做放在一行上來進行復雜的運算。
舉個例子:
// 一行上有三個運算符
for (a = 1, b = 3, c = a * b; a < 10; a++) {
...
}
複製代碼
這樣的技巧在許多 JavaScript 框架中都有使用,這也是爲何咱們提到它。可是一般它並不能提高代碼的可讀性,使用它以前,咱們要想清楚。
先本身作題目再看答案。
重要程度:⭐️⭐️⭐️⭐️⭐️
如下代碼中變量 a
、b
、c
、d
的最終值分別是多少?
let a = 1, b = 1;
let c = ++a; // ?
let d = b++; // ?
複製代碼
重要程度:⭐️⭐️⭐
下面這段代碼運行完成後,代碼中的 a
和 x
的值是多少?
let a = 2;
let x = 1 + (a *= 2);
複製代碼
好好思考一下,把它們寫下來而後和答案比較一下。
答案以下:
a = 2
b = 2
c = 2
d = 1
let a = 1, b = 1;
alert( ++a ); // 2,前置操做符返回最新值
alert( b++ ); // 1,後置操做符返回舊值
alert( a ); // 2,自加一次
alert( b ); // 2,自加一次
複製代碼
答案以下:
a = 4
(乘以 2)x = 5
(至關於計算 1 + 4)現代 JavaScript 教程:開源的現代 JavaScript 從入門到進階的優質教程。React 官方文檔推薦,與 MDN 並列的 JavaScript 學習教程。
在線免費閱讀:zh.javascript.info
掃描下方二維碼,關注微信公衆號「技術漫談」,訂閱更多精彩內容。