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

大家好,我是若川。今天推薦一篇相對簡單些的文章。

大家應該都知道了我最近組織了源碼共讀活動, 有小伙伴表示讀源碼上癮,也很有收獲。工作0-5年都可以參與。感興趣可以加我微信 ruochuan12 私信 源碼 進群。


1. 數值分割符 _

2. 逗號運算符 ,

3. 零合并操作符 ??

4. 可選鏈操作符 ?.

5. 私有方法/屬性

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

7. 位運算符 & 與 |

8. 雙位運算符 ~~

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

10. void 運算符

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  ^: 10100110     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


最近組建了一個杭州的前端交流群,如果你是在杭州工作可以加我微信?ruochuan12?私信 杭州 拉你進群。


推薦閱讀

我在阿里招前端,該怎么幫你(可進面試群)
我讀源碼的經歷

面對 this 指向丟失,尤雨溪在 Vuex 源碼中是怎么處理的
老姚淺談:怎么學JavaScript?

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》多篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,活躍在知乎@若川,掘金@若川。致力于分享前端開發經驗,愿景:幫助5年內前端人走向前列。

識別方二維碼加我微信、拉你進源碼共讀

今日話題

略。歡迎分享、收藏、點贊、在看我的公眾號文章~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275534.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275534.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275534.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Class 創建性能大比拼(反射,泛型反射,泛型創建,緩存Emit,非緩存Emit)

一說到反射&#xff0c;很多人都想到了性能&#xff0c;更有甚者直接說“慎用反射&#xff0c;遺患無窮”&#xff0c;“用反射&#xff0c;感覺怎么像是退步啊&#xff5e;”&#xff0c;看到這種言論&#xff0c;直接把反射妖魔化了&#xff0c;如果這種言論長此以往&#xf…

es6沖刺01

1、let/const 1)作用域&#xff1a;es5中有全局作用域、函數作用域。es6中新增了塊級作用域 2&#xff09;let定義的變量在所在塊級作用域外失效&#xff0c;嚴格模式下失效后直接報錯&#xff0c; 且不允許重復聲明同名變量 3)const用于聲明常量&#xff0c;聲明時必須賦值&am…

linux網卡固件名,修改CentOS7網卡名稱為傳統名稱eth0格式

使用CentOS7以前系統的小伙伴裝完CentOS7以后發現了一個問題&#xff0c;那就是網卡名改變為了“en016777736”&#xff0c;而不是以前的eth0的簡易模式了&#xff0c;如圖&#xff1a;以往的CentOS7以前的系統網卡命名雖然簡單方便&#xff0c;但也會帶來一些問題&#xff0c;…

Baymard Institute:基于UX的最佳實踐的光榮的,循證的工具

重點 (Top highlight)I realized I wanted to write this piece when I mentioned the Baymard Institute to a User Researcher with 10 years of experience and they had no idea what I was talking about. They aren’t alone! I’ve gotten plenty of raised eyebrows on…

Vue 3.2 發布了,那尤雨溪是怎么發布 Vue.js 的?

1. 前言大家好&#xff0c;我是若川。最近組織了源碼共讀活動&#xff0c;感興趣的可以加我微信 ruochuan12&#xff0c;長期交流學習。之前寫的《學習源碼整體架構系列》 包含jQuery、underscore、lodash、vuex、sentry、axios、redux、koa、vue-devtools、vuex4十篇源碼文章。…

wireshark使用教程 linux,Linux入門教程:ubuntu下安裝wireshark(以及配置非root),這個強大的工具可以捕...

Linux入門教程:ubuntu下安裝wireshark(以及配置非root),這個強大的工具可以捕Wireshark是世界上最流行的網絡分析工具。這個強大的工具可以捕捉網絡中的數據&#xff0c;并為用戶提供關于網絡和上層協議的各種信息。與很多其他網絡工具一樣&#xff0c;Wireshark也使用pcap net…

IronPython和C#執行速度對比

其實我自己對執行速度這個問題本來并沒有什么興趣&#xff0c;因為以前的經驗告訴我&#xff1a;除非是運算密集型的程序&#xff0c;否則腳本語言和編譯型語言使用起來速度沒有多大差別。但是我們公司有個人知道我的想法以后&#xff0c;天天在我耳邊嚷嚷腳本運行速度太慢&…

基于超級賬本Fabric的供應鏈跟蹤解決方案【開源】

2019獨角獸企業重金招聘Python工程師標準>>> 本項目為基于Hyperledger Fabric區塊鏈的供應鏈資產跟蹤解決方案&#xff0c;項目主要包括鏈碼和Web應用兩部分。Fabric鏈碼采用GOLANG開發&#xff0c;負責維護資產的狀態&#xff0c;后臺為采用Node.js開發的Web應用&a…

同理心案例及故事分享_神經形態,視覺可及性和同理心

同理心案例及故事分享“A good UX designer has empathy”.“優秀的UX設計人員具有同理心”。 This is something every UX designer has heard at some point in their career. Empathy helps us get into the mindset of the user and build solutions that solve real probl…

純CSS實現beautiful按鈕

大家好&#xff0c;我是若川。邀你進源碼共讀群學習交流。今天分享一篇好文。可收藏&#xff5e;近期工作中遇到一個需求——實現一些酷炫的按鈕&#xff0c;看到效果圖之后&#xff0c;按鈕確實漂亮&#xff0c;有彈跳、顏色漸變、掃光、霓虹燈&#xff0c;瞬間激起了我的好奇…

linux的內核有多小,Linux 內核有小bug?

今天讀著讀著Linux代碼&#xff0c;竟然無意中發現Linux 0.11內核有個小bug&#xff0c;呵呵&#xff0c;人非圣賢孰能無過。// 在目錄項數據塊中搜索匹配指定文件名的目錄項&#xff0c;首先讓de 指向數據塊&#xff0c;并在不超過目錄中目錄項數// 的條件下&#xff0c;循環執…

菜單窗口_菜單

菜單窗口The Hamburger Menu widget is on every other site nowadays. It has become synonymous with the web and, perhaps even more so, with web development. Have, for instance, a look at Dribbble or Codepen. There you’ll find a fair share of examples. They c…

帝國cms 打開打開轉換表文件失敗!

帝國cms 升級到最新版6.6 后 生成列表頁面和 搜索 時出現 “打開打開轉換表文件失敗&#xff01;” 跟蹤文件找到 include($file); 這行代碼時出錯非常納悶&#xff0c;這個是php的內部命令啊&#xff0c;跟帝國的編碼應該沒有關系一直沒有再往下細找&#xff0c;只好根據錯誤提…

怎么在PDF上修改文字,PDF修改文字的步驟

怎么在PDF文件上修改文字呢&#xff1f;其實現在的很多的PDF文件上會出現文字錯誤的情況&#xff0c;想要修改PDF文件上面的文字卻不知道怎么修改&#xff0c;想要修改PDF文件還是比較簡單的&#xff0c;使用專業的PDF編輯器就可以進行操作了&#xff0c;下面小編就為大家分享一…

linux raw限制端口訪出,使用Linux raw socket時需要注意的一些問題

本文的copyleft歸gfree.windgmail.com所有&#xff0c;使用GPL發布&#xff0c;可以自由拷貝&#xff0c;轉載。但轉載請保持文檔的完整性&#xff0c;注明原作者及原鏈接&#xff0c;嚴禁用于任何商業用途。作者&#xff1a;gfree.windgmail.com博客&#xff1a;linuxfocus.bl…

讀完 Vue 發布源碼,小姐姐回答了 leader 的提問,并優化了項目發布流程~

大家好&#xff0c;我是若川。這是 源碼共讀 第三期活動&#xff0c;紀年小姐姐的第三次投稿。紀年小姐姐學習完優化了自己的項目發布流程&#xff0c;而且回答了leader對她的提問&#xff0c;來看看她的思考和實踐。第三期是 Vue 3.2 發布了&#xff0c;那尤雨溪是怎么發布 Vu…

小程序背景圖片的坑

本人是前端菜鳥一個&#xff0c;比小白還要白&#xff0c;這完全是自己的經驗總結&#xff0c;并不是要給各位分享什么寶貴經驗哈&#xff0c;各位大佬不喜勿噴&#xff0c;不然會打擊到我的哈哈因為公司要求做幾個小程序的頁面&#xff0c;我不得不拾起丟棄了幾個月的小程序開…

SimpleAdapter類使用方法

SimpleAdapter的構造函數是&#xff1a; public SimpleAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) 官方說明了其各個參數含義&#xff0c;我這里根據自己的理解解釋下&#xff1a; 第一個context&…

小程序 富文本自適應屏幕_自適應文本:跨屏幕尺寸構建可讀文本

小程序 富文本自適應屏幕Many of you may already know about responsive web design. Cited from Wikipedia, responsive web design (RWD) is an approach to web design that makes web pages render well on a variety of devices and windows or screen sizes. The respon…

Vue、React 之間如何實現代碼移植?

大家好&#xff0c;我是若川。面對前端最火的兩個框架&#xff0c;學 React 還是 Vue &#xff1f;這可能是每個前端人都曾糾結過的問題。不過&#xff0c;現在你不用糾結了——因為很多公司都是兩個框架都有大量的應用&#xff0c;取決于不同團隊的技術選型&#xff0c;特別是…