這些 JS 中強大的操做符,總有幾個你沒據說過


1. 數值分割符 _web

2. 逗號運算符 ,編程

3. 零合併操做符 ??數組

4. 可選鏈操做符 ?.瀏覽器

5. 私有方法/屬性安全

6. 位運算符 >> 與 >>>微信

7. 位運算符 & 與 |編輯器

8. 雙位運算符 ~~函數

9. 短路運算符 && 與 ||post

10. void 運算符flex

11. 其餘經常使用操做符

12. 操做符優先級


JS 裏的操做符你們天天都在使用,還有一些 ES2020、ES2021 新加的實用操做符,這些共同構成了 JS 靈活的語法生態。

本文除介紹經常使用的操做符以外,還會介紹 JS 裏一些不經常使用可是很強大的操做符,下面咱們一塊兒來看看吧~

1. 數值分割符 _

ES2021 引入了數值分割符 _,在數值組之間提供分隔,使一個長數值讀起來更容易。Chrome 已經提供了對數值分割符的支持,能夠在瀏覽器裏試起來。

let number = 100_0000_0000_0000 // 0太多了不用數值分割符眼睛看花了
console.log(number) // 輸出 100000000000000

此外,十進制的小數部分也可使用數值分割符,二進制、十六進制裏也可使用數值分割符。

0x11_1 === 0x111   // true 十六進制
0.11_1 === 0.111 // true 十進制的小數
0b11_1 === 0b111 // true 二進制

2. 逗號運算符 ,

什麼,逗號也能夠是運算符嗎?是的,曾經看到這樣一個簡單的函數,將數組的第一項和第二項調換,並返回兩項之和:

function reverse(arr) {
return [arr[0], arr[1]]=[arr[1], arr[0]], arr[0] + arr[1]
}
const list = [1, 2]
reverse(list) // 返回 3,此時 list 爲[2, 1]

逗號操做符對它的每一個操做數求值(從左到右),並返回最後一個操做數的值。

expr1, expr2, expr3...

會返回最後一個表達式 expr3 的結果,其餘的表達式只會進行求值。

3. 零合併操做符 ??

零合併操做符 ?? 是一個邏輯操做符,當左側的操做數爲 null 或者 undefined 時,返回右側操做數,不然返回左側操做數。

expr1 ?? expr2

空值合併操做符通常用來爲常量提供默認值,保證常量不爲 null 或者 undefined,之前通常使用 || 來作這件事 variable = variable || 'bar'。然而,因爲 || 是一個布爾邏輯運算符,左側的操做數會被強制轉換成布爾值用於求值。任何假值(0''NaNnullundefined)都不會被返回。這致使若是你使用 0''NaN 做爲有效值,就會出現不可預料的後果。

正由於 || 存在這樣的問題,而 ?? 的出現就是解決了這些問題,?? 只會在左側爲 undefinednull 時才返回後者,?? 能夠理解爲是 || 的完善解決方案。

能夠在瀏覽器中執行下面的代碼感覺一下:

undefined || 'default' // 'default'
null || 'default' // 'default'
false || 'default' // 'default'
0 || 'default' // 'default'

undefined ?? 'default' // 'default'
null ?? 'default' // 'default'
false ?? 'default' // 'false'
0 ?? 'default' // 0

另外在賦值的時候,能夠運用賦值運算符的簡寫 ??=

let a = {b: null, c: 10}
a.b ??= 20
a.c ??= 20
console.log(a) // 輸出 { b: 20, c: 10 }

4. 可選鏈操做符 ?.

可選鏈操做符 ?. 容許讀取位於鏈接對象鏈深處的屬性的值,而沒必要驗證鏈中的每一個引用是否有效。?. 操做符的功能相似於 . 鏈式操做符,不一樣之處在於,在引用爲 null 或者 undefined 的狀況下不會引發錯誤,該表達式短路返回值是 undefined

當嘗試訪問可能不存在的對象屬性時,可選鏈操做符將會使表達式更短、更簡明。

const obj = {
a: 'foo',
b: {
c: 'bar'
}
}

console.log(obj.b?.c) // 輸出 bar
console.log(obj.d?.c) // 輸出 undefined
console.log(obj.func?.()) // 不報錯,輸出 undefined

之前可能會經過 obj && obj.a && obj.a.b 來獲取一個深度嵌套的子屬性,如今能夠直接 obj?.a?.b 便可。

可選鏈除了能夠用在獲取對象的屬性,還能夠用在數組的索引 arr?.[index],也能夠用在函數的判斷 func?.(args),當嘗試調用一個可能不存在的方法時也可使用可選鏈。

調用一個對象上可能不存在的方法時(版本緣由或者當前用戶的設備不支持該功能的場景下),使用可選鏈可使得表達式在函數不存在時返回 undefined 而不是直接拋異常。

const result = someInterface.customFunc?.()

5. 私有方法/屬性

在一個類裏面能夠給屬性前面增長 # 私有標記的方式來標記爲私有,除了屬性能夠被標記爲私有外,getter/setter 也能夠標記爲私有,方法也能夠標爲私有。

class Person {
getDesc(){
return this.#name +' '+ this.#getAge()
}

#getAge(){ return this.#age } // 私有方法

get #name(){ return 'foo' } // 私有訪問器
#age = 23 // 私有屬性
}
const a = new Person()
console.log(a.age) // undefined 直接訪問不到
console.log(a.getDesc()) // foo 23

6. 位運算符 >> 與 >>>

有符號右移操做符 >> 將第一個操做數向右移動指定的位數,多餘的位移到右邊被丟棄,高位補其符號位,正數補 0,負數則補 1。由於新的最左位與前一個最左位的值相同,因此符號位(最左位)不會改變。

(0b111>>1).toString(2)   // "11"
(-0b111>>1).toString(2) // "-100" 感受跟直覺不同

正數的好理解,負數怎麼理解呢,負數在計算機中存儲是按照補碼來存儲的,補碼的計算方式是取反加一,移位時將補碼形式右移,最左邊補符號位,移完以後再次取反加一求補碼得到處理後的原碼。

-111      // 真值
1 0000111 // 原碼(高位的0無所謂,後面加不到)
1 1111001 // 補碼
1 1111100 // 算數右移
1 0000100 // 移位後求補碼得到原碼
-100 // 移位後的真值

通常咱們用 >> 來將一個數除 2,至關於先捨棄小數位而後進行一次 Math.floor

10 >> 1    // 5
13 >> 1 // 6 至關於
13.9 >> 1 // 6
-13 >> 1 // -7 至關於
-13.9 >> 1 // -7

無符號右移操做符 >>>,將符號位做爲二進制數據的一部分向右移動,高位始終補 0,對於正整數和算數右移沒有區別,對於負數來講因爲符號位被補 0,成爲正數後就不用再求補碼了,因此結果老是非負的。即使右移 0 個比特,結果也是非負的。

(0b111>>>1).toString(2)   // "11"
(-0b111>>>1).toString(2) // "1111111111111111111111111111100"

能夠這樣去理解

-111      // 真值
1 000000000000000000000000000111 // 原碼
1 111111111111111111111111111001 // 補碼
0 111111111111111111111111111100 // 算數右移(因爲右移後成爲正數,就不要再求補碼了)
1073741820 // 移位後的真值

左移運算符 << 與之相似,左移很簡單左邊移除最高位,低位補 0:

(0b1111111111111111111111111111100<<1).toString(2)   // "-1000"
(0b1111111111111111111111111111100<<<1).toString(2) // "-1000"

PS:JS 裏面沒有無符號左移,並且其餘語言好比 JAVA 也沒有無符號左移。

7. 位運算符 & 與 |

位運算符是按位進行運算,& 與、| 或、~ 非、^ 按位異或:

&: 1010  |: 1010  ~: 1010  ^: 1010
0110 0110 0110
---- ---- ---- ----
0010 1110 0101 1100

使用位運算符時會拋棄小數位,咱們能夠利用這個特性來給數字取整,好比給任意數字 & 上二進制的 32 個 1,或者 | 上 0,顯而易見後者簡單些。

因此咱們能夠對一個數字 | 0 來取整,負數也一樣適用

1.3 | 0         // 1
-1.9 | 0 // -1

判斷奇偶數除了常見的取餘 % 2 以外,也可使用 & 1,來判斷二進制數的最低位是否是 1,這樣除了最低位以外都被置 0,取餘的結果只剩最低位,是否是很巧妙。負數也一樣適用:

const num = 3
!!(num & 1) // true
!!(num % 2) // true

8. 雙位運算符 ~~

可使用雙位操做符來替代正數的 Math.floor( ),替代負數的 Math.ceil( )。雙否認位操做符的優點在於它執行相同的操做運行速度更快。

Math.floor(4.9) === 4      // true
// 簡寫爲:
~~4.9 === 4 // true

不過要注意,對正數來講 ~~ 運算結果與 Math.floor( ) 運算結果相同,而對於負數來講與 Math.ceil( ) 的運算結果相同:

~~4.5                // 4
Math.floor(4.5) // 4
Math.ceil(4.5) // 5

~~-4.5 // -4
Math.floor(-4.5) // -5
Math.ceil(-4.5) // -4

PS:注意 ~~(num/2) 方式和 num >> 1 在值爲負數時的差異

9. 短路運算符 && 與 ||

咱們知道邏輯與 && 與邏輯或 || 是短路運算符,短路運算符就是從左到右的運算中前者知足要求,就再也不執行後者了。

能夠理解爲:

  • && 爲取假運算,從左到右依次判斷,若是遇到一個假值,就返回假值,之後再也不執行,不然返回最後一個真值
  • || 爲取真運算,從左到右依次判斷,若是遇到一個真值,就返回真值,之後再也不執行,不然返回最後一個假值
let param1 = expr1 && expr2
let param2 = expr1 || expr2
短路運算符

所以能夠用來作不少有意思的事,好比給變量賦初值:

let variable1
let variable2 = variable1 || 'foo'

若是 variable1 是真值就直接返回了,後面短路就不會被返回了,若是爲假值,則會返回後面的foo

也能夠用來進行簡單的判斷,取代冗長的if語句:

let variable = param && param.prop
// 有了可選鏈以後能夠直接 param?.prop

若是 param 若是爲真值則返回 param.prop 屬性,不然返回 param 這個假值,這樣在某些地方防止 paramundefined 的時候還取其屬性形成報錯。

10. void 運算符

void 運算符 對給定的表達式進行求值,而後返回 undefined

能夠用來給在使用當即調用的函數表達式(IIFE)時,能夠利用 void 運算符讓 JS 引擎把一個 function 關鍵字識別成函數表達式而不是函數聲明。

function iife() { console.log('foo') }()       // 報錯,由於JS引擎把IIFE識別爲了函數聲明
void function iife() { console.log('foo') }() // 正常調用
~function iife() { console.log('foo') }() // 也可使用一個位操做符
(function iife() { console.log('foo') })() // 或者乾脆用括號括起來表示爲總體的表達式

還能夠用在箭頭函數中避免傳值泄漏,箭頭函數,容許在函數體不使用括號來直接返回值。這個特性給用戶帶來了不少便利,但有時候也帶來了沒必要要的麻煩,若是右側調用了一個本來沒有返回值的函數,其返回值改變後,會致使非預期的反作用。

const func = () => void customMethod()   // 特別是給一個事件或者回調函數傳一個函數時

安全起見,當不但願函數返回值是除了空值之外其餘值,應該使用 void 來確保返回 undefined,這樣,當 customMethod 返回值發生改變時,也不會影響箭頭函數的行爲。

11. 其餘經常使用操做符

  1. 三元表達式:很簡單了,你們常常用, expr ? expr1 : expr2 若是 expr 爲真值則返回 expr1,不然返回 expr2
  2. 賦值運算符簡寫:加法賦值 +=、減法賦值 -=、乘法賦值 *=、除法賦值 /=、求冪賦值 **=、按位或複製 |=、按位與賦值 &=、有符號按位右移賦值 >>=、無符號按位右移賦值 >>>=、邏輯空賦值 ??= ....
  3. 求冪運算符var1 ** var2 至關於 Math.pow,結果爲 var1var2 次方

12. 操做符優先級

正由於有操做符優先級,因此 variable = 1, 2 的含義是將變量先賦值爲 1,再返回數字 2,而不是變量賦值給 1, 2 的返回值 2,這是由於 = 運算符的優先級高於 , 逗號運算符。再好比表達式 6 - 2 * 3 === 0 && 1- * === && 這四個運算符優先級最高的 * 先運算,而後 - 運算符結果爲 0,=== 運算符優先級高於 &&true && 1 的結果爲 1,因此這就是運算的結果。

下面的表將運算符按照優先級的不一樣從高(20)到低(1)排列,但這個不是最新的,至少沒包括可選鏈,建議參考這個表[1]或者 MDN[2]

運算符優先級

參考文檔:

  1. 運算符優先級 - JavaScript | MDN [3]
  2. JS 中能夠提高幸福度的小技巧 [4]
  3. 4個未據說過的強大JavaScript操做符
  4. 聊聊JavaScript中的二進制數 [5]

PS:本文收錄在在下的博客 Github - SHERlocked93/blog[6] 系列文章中,歡迎 star~

參考資料

[1]

運算符優先級 - JavaScript | MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

[2]

JS 中能夠提高幸福度的小技巧: https://juejin.cn/post/6844903641468403726

[3]

聊聊JavaScript中的二進制數: https://zhuanlan.zhihu.com/p/22297104


本文分享自微信公衆號 - 編程微刊(wangxiaoting678)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索