現代 JavaScript 教程 — 那些你應該注意的運算符

運算符

咱們從學校裏瞭解到過不少運算符,好比說加號 +、乘號 *、減號 - 等。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,而後將結果賦值給左側:cba。最後,全部的變量都共享一個值。

賦值運算符 \"=\" 會返回一個值

每一個運算符都有一個返回值。對於以加號 + 或者乘號 * 爲例的大部分運算符而言,這一點很顯然。對於賦值運算符而言,這一點一樣適用。

語句 x = valuevalue 的值寫入 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 中的。

對於天然數 ba ** b 的結果是 a 與本身相乘 b 次。

舉個例子:

alert( 2 ** 2 ); // 4 (2 * 2)
alert( 2 ** 3 ); // 8 (2 * 2 * 2)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
複製代碼

這個運算符對於 ab 是非整數的狀況依然適用。

例如:

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

二者都作同一件事:將變量 counter1 相加。

那麼他們有區別嗎?有,但只有當咱們使用 ++/-- 的返回值時才能看到區別。

詳細點說。咱們知道,全部的運算符都有返回值。自加/自減也不例外。前置形式返回一個新的值,但後置返回原來的值(作加法/減法以前的值)。

爲了直觀看到區別,看下面的例子:

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 框架中都有使用,這也是爲何咱們提到它。可是一般它並不能提高代碼的可讀性,使用它以前,咱們要想清楚。

做業題

先本身作題目再看答案。

1. 後置操做符和前置操做符

重要程度:⭐️⭐️⭐️⭐️⭐️

如下代碼中變量 abcd 的最終值分別是多少?

let a = 1, b = 1;

let c = ++a; // ?
let d = b++; // ?
複製代碼

2. 賦值結果

重要程度:⭐️⭐️⭐

下面這段代碼運行完成後,代碼中的 ax 的值是多少?

let a = 2;

let x = 1 + (a *= 2);
複製代碼

好好思考一下,把它們寫下來而後和答案比較一下。

答案

1. 後置操做符和前置操做符

答案以下:

  • 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,自加一次
複製代碼

2. 賦值結果

答案以下:

  • a = 4(乘以 2)
  • x = 5(至關於計算 1 + 4)

現代 JavaScript 教程:開源的現代 JavaScript 從入門到進階的優質教程。React 官方文檔推薦,與 MDN 並列的 JavaScript 學習教程

在線免費閱讀:zh.javascript.info


掃描下方二維碼,關注微信公衆號「技術漫談」,訂閱更多精彩內容。

相關文章
相關標籤/搜索