【JS 口袋書】第 7 章:JS 中的類型轉換與比較

做者:valentinogagliardihtml

譯者:前端小智前端

來源:githubgit


阿里雲最近在作活動,低至2折,有興趣能夠看看: promotion.aliyun.com/ntms/yunpar…es6


爲了保證的可讀性,本文采用意譯而非直譯。github

JS 中的基本類型

JS 有 7 種基本類型,分別以下:算法

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Object
  • Symbol (ES6)

布爾值表示的值能夠是 true ,也能夠是 false。另外一方面,null 是故意缺乏一個值。null 一般被賦值給一個變量,用來表示變量事後會被賦予值。編程

var maybe = null;
複製代碼

而後是 undefined,表示是一個變量沒有任何附加項:數組

var name;
console.log(name)
undefined
複製代碼

nullundefined 看起來很類似,但它們是兩個大相徑庭的類型,以致於開發人員仍不肯定要使用哪一個類型。微信

可使用 typeof 操做符來查看變量的類型:編程語言

typeof "alex"
"string"
複製代碼

number 類型:

typeof 9
"number"
複製代碼

boolean 類型:

typeof false
"boolean"
複製代碼

undefined 類型:

typeof undefined
"undefined"
複製代碼

null 類型

typeof null
"object"
複製代碼

這個結果有點奇怪。null 看起來像一個對象,但實際上它是JS的一個歷史錯誤,自該語言誕生以來就一直存在。因爲這些緣由,JS 一直名聲不佳。null 只是其中的一例。另外,一種類型和另外一種類型之間的轉換有一些奇怪的規則。

先給你們介紹一下背景。各位先用 Python 作一個例子。Python 中的如下指令

'hello' + 89
複製代碼

這樣會獲得一個明確的錯誤:

TypeError: can only concatenate str (not "int") to str
複製代碼

在 JS 中徹底沒有問題:

'hello' + 89
複製代碼

結果:

"hello89"
複製代碼

更加奇怪是直接加一個數組:

'hello' + []
複製代碼

結果:

'hello'
複製代碼

再來:

'hello' + [89]
複製代碼

結果:

"hello89"
複製代碼

看起來這種轉換背後有某種邏輯,甚至還能夠有更加複雜的數組結構:

'hello' + [89, 150.156, 'mike']
複製代碼

結果:

"hello89,150.156,mike"
複製代碼

這兩行 JS 足以讓 Java 開發人員望而卻步。可是 JS 中的這種行爲是100%故意的。所以,有必要研究一下 JS 中隱式轉換(也稱爲類型強制轉換)。

當數字變成字符串

一些編程語言有一個稱爲類型轉換的概念,這意味着:若是我們想將一個類型轉換成另外一種類型,那麼必須使轉換明確。在 JS 中也有提供這種方法。考慮如下示例

var greet = "Hello";
var year = 89;
複製代碼

若是想進行顯式轉換,能夠在代碼中用 toString() 方法:

var greet = "Hello";
var year = 89;

var yearString = year.toString()
複製代碼

或者使用 String

var greet = "Hello";
var year = 89;

var yearString = String(year)
複製代碼

String 是JS 內置對象的一部分,它反映了一些基本類型:StringNumberBooleanObject。這些內置組件可用於類型之間的轉換。轉換後,我們能夠拼接兩個變量

greet + yearString;
複製代碼

可是除了這種顯式轉換以外,在 JS 中還有一種微妙的機制,稱爲隱式轉換,由 JS 引擎提供。

'hello' + 89
複製代碼

結果:

"hello89"
複製代碼

可是這種轉換背後的邏輯是什麼? 你可能會驚訝地發現,若是 JS 中的加法運算符+中的一個是字符串,則會自動將兩個操做數中的任何一個轉換爲字符串!

更使人驚訝的是,這個規則在ECMAScript規範中已經固定下來了。第11.6.1節定義了加法運算符的行爲,在這裏總結一下:

加法運算符(+) 若是 x 是字符串或者 y 是字符串那麼返回 ToString(x) 後面跟 ToString(y)

這種把戲只對數字有效嗎? 不是,數組和對象同樣的,跑不掉:

'hello' + [89, 150.156, 'mike']
複製代碼

結果:

"hello89,150.156,mike"
複製代碼

對象怎樣:

'hello' + { name: "Jacopo" }
複製代碼

結果:

"hello[object Object]"
複製代碼

爲了弄清,咋肥事,能夠經過將對象轉換爲字符串來進行快速測試:

String({ name: "Jacopo" })
複製代碼

結果:

"[object, Object]"
複製代碼

但還有另外一個問題:乘法、除法和減法的狀況又是腫麼樣的?

我不是一個數字!

我們看到加法運算符在至少一個操做數是字符串時,是如何將操做數轉換成字符串。可是其餘的算術運算符呢?

操做符 描述
+
++ 自增
*
** 指數 (es6)
-
-- 自減
/
% 取餘

若是對不是數字的類型使用其中一個操做符(+除外),那麼就獲得了一種特殊類型的 : NaN

89 ** "alex"
複製代碼

結果:

NaN
複製代碼

NaN 表示不是數字,任何失敗的算術運算,以下面的代碼所示:

var obj = { name: "Jacopo" } % 508897
複製代碼

結果:

console.log(obj)
NaN
複製代碼

注意與NaN結合的 typeof。這個代碼看起來沒問題:

typeof 9 / "alex"
NaN
複製代碼

那下面呢?

var strange = 9 / "alex"
複製代碼

再使用 typeof:

typeof strange
"number"
複製代碼

NaN 被分配給一個變量時,它就變成了number,這就引出了一個新問題。我如何檢查一些變量是不是 NaN? ES6 中有一個名爲 isNaN() 的新方法:

var strange = 9 / "alex"
isNaN(strange)
true
複製代碼

接着來看看 JS 中的比較運算符,它們和算術運算符同樣奇怪。

相等仍是不等

JS 中有兩大類比較運算符。首先是所說的**「弱比較」**。它是一個抽象的比較運算符(雙等號):==。而後還有一個「強比較」:===,又名 嚴格比較運算符。他倆兄弟的行爲方式不同,來看看一些例子。

首先,若是我們用兩個操做符比較兩個字符串,倆兄弟獲得一致的結果:

"hello" == "hello"
true

"hello" === "hello"
true
複製代碼

看起來很 nice,如今來比較兩種不一樣的類型,數字和字符串。

首先是「強比較」

"1" === 1
false
複製代碼

很明顯,字符串 1 等於數字 1。弱比較又是怎麼樣?

"1" == 1
true
複製代碼

true表示這兩個值相等。這種行爲與我們前面看到的隱式轉換有關。原來,抽象比較操做符會在比較類型以前自動轉換類型。這是一個摘要:

抽象等式比較算法 比較 x == y 是這樣執行的:若是 x 是字符串,y 是數字,返回比較的結果ToNumber(x) == y

說白了就是:若是第一個操做數是字符串,第二個操做數是數字,那麼將第一個操做數轉換爲數字。

有趣的是,JS 規範中充滿了這些瘋狂的規則,我強烈建議對此進行更深刻的研究。固然如今都建議使用強比較。

嚴格相等比較的規範指出,在將值與===進行比較以前,不會進行自動轉換。在代碼中使用嚴格的相等比較能夠避免愚蠢的錯誤。

基本數據類型與對象

我們已經看到了 JS 的構建塊:StringNumberBoolean、Null、UndefinedObjectSymbol。它們都是大寫的,這種風格甚至出如今ECMAScript規範中。可是除了這些基本類型以外,還有一些鏡像原語的雙胞胎:內置對象。例如,String 類型有一個等效的 String ,它以兩種方式使用。若是像函數那樣調用(經過傳遞參數),它會將任何值轉換成字符串:

var someValue = 555;

String(someValue);

"555"
複製代碼

若是使用 String 做爲 new 的構造函數,那麼結果就是String類型的對象

var someValue = 555;

var newString = new String(someValue);
複製代碼

這種方式值得在控制檯中查看對象,看看它與「普通」字符串有何不一樣

可使用 typeof 來確認它確實是一個對象:

typeof newString
"object"
複製代碼

基本類型的 Number 也有一個內置對象 Number,它能夠(幾乎)將任何值轉換爲數字:

var someValue = "555";

Number(someValue);

555;
複製代碼

我說的幾乎是由於在試圖轉換無效的「數字」時獲得 NaN

var notValidNumber = "aa555";

Number(notValidNumber);

NaN;
複製代碼

在構造函數形式Number中使用時,將返回number類型的新對象:

var someValue = "555";

new Number(someValue);

Number {555}
複製代碼

內置對象 Boolean 以將任何值轉換成布爾值:

var convertMe = 'alex';

Boolean(convertMe)

true
複製代碼

用構造函數的方式會返回一個對象:

var convertMe = 'alex';

typeof new Boolean(convertMe)

"object"
複製代碼

內置的 Object 行爲也同樣:

Object('hello'); // String {"hello"}

Object(1); // Number{1}
複製代碼

若是在沒有參數的狀況下調用,它將返回一個空對象

Object()

{}
複製代碼

若是做爲構造函數調用,則返回一個新對象

new Object({
  name: "Alex",
  age: 33
});

{name: "Alex", age: 33}
複製代碼

此時,你可能會問本身:何時可使用內置對象,何時應該使用基本類型初始化值? 根據經驗,當你只須要一個簡單的類型時,應該避免構造函數調用:

// 不要這麼用
var bool = new Boolean("alex");
var str = new String('hi');
var num = new Number(33);
var strObj = new Object('hello')
複製代碼

除了不實用以外,這種形式還會帶來性能損失,由於這種方式每次都會建立一個新對象。 最後但一樣重要的是,當我們想要一個簡單的字符串或數字時,建立一個對象是沒有意義的。因此下面的形式是首選的

// ok 的
var bool = true
var str = 'hi';
var num = 33;
var obj = { name: "Alex", age: 33 };
複製代碼

當須要轉換某些內容時,能夠像使用函數同樣使用 BooleanStringNumber,。

總結

JS 中有七個構建塊,即 StringNumberBooleanNullUndefinedObjectSymbol,這些也稱爲基本類型。

JS 開發人員可使用算術和比較操做符操做這些類型。可是我們須要特別注意加法運算符+和抽象比較運算符 ==,它們本質上傾向於在類型之間進行轉換。

這種 JS 中的隱式轉換稱爲類型強制轉換,並在ECMAScript規範中定義。建議在代碼中始終使用嚴格的比較操做符===代替 ==

做爲一種最佳實踐,當你打算在一種類型和另外一種類型之間進行轉換時,必定要弄清楚彼此之間的類型。爲此,JS 有一堆內建對象,它們基本類型的一種映射:StringNumberBoolean。這些內置函數可用於顯式地在不一樣類型之間進行轉換。

思考題:

  1. 44 - "alex" 的輸出結果是?

  2. 44 + "alex" 的輸出結果是?爲啥?

  3. "alex" + { name: "Alex" } 的輸出結果是 ?

  4. ["alex"] == "alex" 輸出結果是啥?爲何呢?

  5. undefined == null 輸出結果是啥?爲何呢?

代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug

原文:github.com/valentinoga…

交流(歡迎加入羣,羣工做日都會發紅包,互動討論技術)

阿里雲最近在作活動,低至2折,有興趣能夠看看:promotion.aliyun.com/ntms/yunpar…

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

github.com/qq449245884…

由於篇幅的限制,今天的分享只到這裏。若是你們想了解更多的內容的話,能夠去掃一掃每篇文章最下面的二維碼,而後關注我們的微信公衆號,瞭解更多的資訊和有價值的內容。

clipboard.png

每次整理文章,通常都到2點才睡覺,一週4次左右,挺苦的,還望支持,給點鼓勵

相關文章
相關標籤/搜索