天天看點

千萬别小看這些運算符背後的邏輯

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

最近回顧javascript的一些基礎知識點時,引起的思考确實颠覆了我之前的一些認知。我清楚地記得曾多次在網上看到一些奇奇怪怪的表達式,它們的運算結果着實讓人懵逼。就比如我在js資料類型很簡單,卻也不簡單這一篇筆記中提到的[] == ![]這樣一個表達式,它的運算結果是true。如果你不細緻地去研究它背後的運算邏輯,你隻會驚呼”這是什麼鬼“?相反,當你靜下心來看清楚它的運算邏輯後,你會感歎“妙哉妙哉”!沒錯,本文的主角就是這些容易讓人小觑的運算符。

加法運算符+

首先說的是加法運算符+,這是一個很容易被人忽視的運算符。我們知道,+可以用來做數字運算,也可以用作字元串拼接,但是還有一些細節可能是大家不知道的。如果+運算符的兩個操作數類型不一緻,或者說兩個操作數既不是字元串也不是數字,那麼它的運算規則是什麼?

先舉幾個例子,你可以先思考下這些運算結果分别是什麼。

千萬别小看這些運算符背後的邏輯

其實規則很簡單,我們隻要簡單地列舉出資料類型的可能性,就幾乎得到了完整的答案。

  • 如果操作數都是數字,進行數字的加法運算。
  • 如果操作數都是字元串,進行字元串的拼接。
  • 如果操作數是對象,會轉換為原始值(一般是先調用valueOf(),日期對象比較特殊,會調用toString()),得到的原始值不再被強制轉換為數字或字元串。在這種限制下,對象轉為原始值基本都是字元串(如果你沒有重寫valuOf()或者toString()方法),根據下面的第四點,會執行字元串拼接操作。
  • 如果其中一個操作數是字元串,另一個操作數也會被轉為字元串,+運算符執行字元串拼接操作。
  • 如果兩個操作數都不是字元串或對象,則會進行算術加法運算(非數字的操作數會被強制轉為數字)。

是以,不難得出上面列舉的表達式的運算結果。

千萬别小看這些運算符背後的邏輯

要記住這些規則并不簡單,一個記憶技巧是:+運算符偏愛字元串拼接操作。

相等運算符==

這個運算符的運算規則,在js資料類型很簡單,卻也不簡單這篇筆記中已經簡單地解釋過了。其實隻要記住一條規則:對于==運算符,如果兩個操作數是null或undefined,運算結果是true;否則,不管操作數的類型如何轉換,==運算符最後都是數字的比較。

舉幾個簡單的例子說明下:

千萬别小看這些運算符背後的邏輯

比較運算符

大于>,大于等于>=,小于<,小于等于<=,用于比較數字的大小或字元在字母表中的排序。要注意的是,在ASCII中,大寫字母排在小寫字母前面。

這些比較運算符更偏愛數字的比較,除非兩個操作數都是字元串。

對于字元串比較的情況,如果兩個字元串的第一個字元是相同的,則會比較第二個字元,以此類推。

這裡有一個比較特殊的NaN,它與任何值做比較都會傳回false。

千萬别小看這些運算符背後的邏輯

位運算符

位運算符很少用到,但是弄明白它們的運算邏輯是很有必要的。位運算符主要分為與&、或|、非~、異或^以及左移<<、帶符号右移>>、無符号右移>>>等。

位運算符都是二進制的運算,并且是基于32位整數運算。是以十進制,十六進制的操作數都會先轉為32位的二進制後再進行運算。這裡以0x1234 & 0x00FF = 0x0034為例說明下流程:

  • 0x123轉為二進制是0000 0000 0000 0000 0001 0010 0011 0100,0x00FF轉為二進制是0000 0000 0000 0000 0000 0000 0011 0100。
  • 進行按位與操作,結果是0000 0000 0000 0000 0000 0000 0011 0100,最後轉為十六進制就是0x0034。

移位運算符

在複習到移位運算符這塊時,我不由得提出了一個疑問:“javascript中為什麼沒有無符号左移運算符?”要解答這樣一個疑問,首先還是要看看左移和右移分别是怎麼運算的。

摘取《計算機組成原理教程》書中的一段描述:

計算機中機器數的字長往往是固定的,當機器數左移n位或右移n位時,必然會使其n位低位或n位高位出現空位。那麼,對空出的空位應該添補0還是1呢?這與機器數采用有符号數還是無符号數有關。對無符号數的移位稱為邏輯移位,對有符号數的移位稱為算術移位。

注意:在javascript中,移位運算符隻支援移動0~31位,如果移動的位數超過了31位,位數會取模MOD 32。也就是說:

千萬别小看這些運算符背後的邏輯

帶符号右移>>

對于帶符号右移(算術右移)運算而言,第一個操作數是有符号數,它的最高位代表符号位,在移位後的符号位不改變。簡單總結就是“低位舍棄,高位補符号位”。

千萬别小看這些運算符背後的邏輯

如果你自己寫幾個右移運算表達式做試驗,你就會産生一個疑惑,為什麼有的正數在帶符号右移後卻變成了負數,比如下面這個:

千萬别小看這些運算符背後的邏輯

這是因為32位的最大帶符号正整數是231 - 1,即2147483647,轉換為二進制是0111 1111 1111 1111 1111 1111 1111 1111。正數的補碼與原碼相同,2147483648相當于在此基礎上加1,就得到補碼1000 0000 0000 0000 0000 0000 0000 0000,而這個補碼是一個非常特殊的碼,它沒有對應的原碼和補碼,代表32位能表示的帶符号數中最小的負數231 - 1,即-2147483648。而2147483648在32位帶符号正數中是無法表示的,其值已經溢出了。

千萬别小看這些運算符背後的邏輯

計算機隻了解二進制,與人類所了解的十進制之間永遠存在一個精度問題,需要足夠的精度才能更加準确地表示十進制,而計算機的位數永遠都是有限的,這就是沖突存在的地方,是以會出現溢出這種現象。

就好比時鐘一般,23時結束了又從0時開始。在帶符号二進制表示法中,正數和負數首尾相連,形成一個環,在計算機可表示的範圍内,溢出的那個數字在某種意義上能在另一個起點找到。

千萬别小看這些運算符背後的邏輯

是以,下面的位運算表達式也是等價的:

千萬别小看這些運算符背後的邏輯

無符号右移>>>

無符号右移也稱為邏輯右移。無符号右移的移位過程中,符号位可能會改變。是以移位後,原來的負數可能變成正數。可以簡單記憶為“低位舍棄,高位補0”。

千萬别小看這些運算符背後的邏輯

左移<<

翻閱《計算機組成原理教程》可以發現,書中有描述到算術左移和邏輯左移。也就是說,左移也分帶符号左移和無符号左移。經測試,javascript中的左移運算符<<一般不會改變符号位,意味着它是算術左移(其實對比<<和>>也能知道,<<是帶符号左移)。

但是左移也要注意溢出的情況,比如:

千萬别小看這些運算符背後的邏輯

那麼為什麼javascript中卻沒有邏輯左移呢?我找了一些資料,比如es5規範和注解,還有一些javascript的書籍,都沒有找到解釋。是以這裡也沒有一個權威的答案(如果有大佬知道的話,請不吝賜教)。

我個人的想法是,應該是要回到移位運算的本質。

二進制表示的機器數在相對于小數點作n位左移或右移時,其實質就是該數乘以或除以2n(n=1,2, …, n)。

而在左移過程中,如果把符号位都丢了,就失去了乘以2n的意義了。是以不隻是javascript,其他程式設計語言如java等也沒有邏輯左移運算符。

最後

不得不說,大學課程真的很重要。如果一直都保持對計算機基礎課程的關注,相信了解這些程式設計語言背後的本質會變得輕松很多。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/live

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-06-05

本文作者:飛白丶

本文來自:“

掘金

”,了解相關資訊可以關注“掘金”