從 [] == ![] 看 js 類型轉(zhuǎn)換

很久之前箕戳,看到過這樣一種判斷

[] == ![];    // true

當(dāng)時覺得很神奇某残,翻了些博客,但也似懂非懂陵吸。今天翻看博客的時候玻墅,偶然又看見了它,感覺跟以前比更清晰了些壮虫,所以在此結(jié)合 js 類型轉(zhuǎn)換澳厢,記錄下自己的理解。
[TOC]

一旨指、分解

乍一看赏酥,確實比較容易讓人迷惑,但是復(fù)雜的東西只要能夠被分解谆构,我們就能容易地分析、理解它了框都。

由 JS 運算符優(yōu)先級可知搬素,“!” 的優(yōu)先級高于 “==”,也就是說魏保,一定程度上熬尺,我們可以將上面的語句改寫為:

[] == (![]);    // true

或者,更進一步:

var a = [], b = ![];
a == b;    // true

二谓罗、求值
其實到上一步的時候,你可能已經(jīng)發(fā)現(xiàn)了一個問題,沒錯伞剑,那就是:

var b = ![];   // false

其實這里就涉及到對象類型的真假值的問題驯绎。JavaScript 規(guī)定,所有的 JavaScript 的引用類型數(shù)據(jù)都是真值刻蚯,這里說“引用類型數(shù)據(jù)”而非對象類型绊含,是因為

typeof null === 'object';  // true
!null === true;    // true

筆者并不想糾結(jié)于 null 是不是對象類型這樣的問題進行討論。
正如上文所述炊汹,任何引用類型的數(shù)據(jù)都是真值躬充,包括對象、函數(shù)等等:

!{} === true;   // false
![] === false;  // true
!function () {} === false;   // true
!window === true;    // false
!window.open === false;   // true

注意這里說的是任何引用類型,類似 Boolean 此類的包裝函數(shù)所構(gòu)造出來的對象當(dāng)然也在此列:

!new Boolean(0) === false;   // true
!new Boolean(true) === true;   // false

有時我們想要將某種數(shù)據(jù)快速轉(zhuǎn)換為一個布爾值充甚,而不想因為使用 Boolean 而導(dǎo)致數(shù)據(jù)變成對象的時候以政,可以使用如下形式:

!!0    // false
!!undefined   // false
!!1   // true
!!{}   // true
!![]    // true
!!false  // false
!!null    // false
!!function () {}   // true

順帶一提的是,出了 ! 會導(dǎo)致真假值的判斷伴找,直接將一個值作為 if 語句的判斷條件也會如此妙蔗。

if (condition) {
  // do something
}

這里只要 condition 是一個真值,if 分支下的 “do something” 處的語句就會被執(zhí)行疆瑰。最典型的坑就出在 condition 是一個

  • 空數(shù)組([])
  • 空的 NodeList 實例( 如 document.querySelectorAll('not-exist') )
  • 空的 HTMLCollection 實例( 如 document.getElementsByTagName('not-exist') )
  • 空的 jQuery 實例( 如 $('not-exist') )...

因為 document.getElementById 沒有命中的時候返回 null眉反,也即是一個假值,很多人認(rèn)為 document.getElementsByTagName穆役、jQuery 等也是如此寸五,或者認(rèn)為它們返回了一個空的數(shù)組,并認(rèn)為這個空數(shù)組“應(yīng)該”是一個假值耿币。但實際上梳杏,無論是空數(shù)組、還是空的 NodeList / HTMLCollection / jQuery 的實例淹接,它們本質(zhì)上都還是引用類型數(shù)據(jù)十性,所以它們都是真值。一個比較簡單的驗證 NodeList / HTMLCollection / jQuery 的實例是否命中的方法是讀取它們的 length 屬性塑悼,如果不為 0 劲适,則可以認(rèn)為它們命中了元素。

三厢蒜、toString / valueOf
經(jīng)過前兩步的分析霞势,我們可以將前面的判斷改寫為:

[] == false;      // true

按照常規(guī)思路,引用類型的變量之間的比較斑鸦,是基于引用的比較愕贡,二者如果是相同的引用,則相等巷屿,否則不等固以。如果按照這樣的邏輯,引用類型的數(shù)據(jù)根本不可能和基礎(chǔ)類型的數(shù)據(jù)相等才對嘱巾,但是這里就真的相等了憨琳。
說到這里,就必須提到原生 JS 中 toString / valueOf 這兩個處處遍布的方法浓冒。

(一) 分類

對于不同類型的對象栽渴,js定義了多個版本的 toString 和 valueOf 方法

(1) toString:

  • 普通對象,返回 "[object Object]";
  • 數(shù)組稳懒,返回數(shù)組元素之間添加逗號合并成的字符串;
  • 函數(shù)闲擦,返回函數(shù)的定義式的字符串;
  • 日期對象慢味,返回一個可讀的日期和時間字符串;
  • 正則,返回其字面量表達式構(gòu)成的字符串;

(2) valueOf:

  • 日期對象墅冷,返回自1970年1月1日到現(xiàn)在的毫秒數(shù);
  • 其它均返回對象本身;

toString / valueOf 兩個方法纯路,主要可用于引用類型數(shù)據(jù)的類型轉(zhuǎn)換,通過調(diào)用它們寞忿,可以將引用類型數(shù)據(jù)使用在原本應(yīng)該使用基本數(shù)據(jù)類型的地方驰唬。

(二)適用場景

原生的 toString / valueOf 分別位于對象的構(gòu)造函數(shù)的 prototype 屬性上,如果需要修改腔彰,大可直接在實例對象上直接添加 toString / valueOf 方法叫编,這樣也不會影響到原型鏈上的方法。

(1)類型轉(zhuǎn)換

1)對象=>字符串
a. 執(zhí)行toString霹抛,如果返回了一個原始值搓逾,則將其轉(zhuǎn)化為字符串
b. 否則執(zhí)行valueOf方法,如果返回了一個原始值杯拐,則將其轉(zhuǎn)化為字符串
c. 否則拋出類型錯誤
如:

var o = {};
o.toString = function () {
  return 'my string';
};
String(o);      // my string

2) 對象=>數(shù)字
a. 執(zhí)行valueOf霞篡,如果返回了一個原始值,如果需要端逼,則將其轉(zhuǎn)化為數(shù)字
b. 否則執(zhí)行toString朗兵,如果返回了一個原始值,則將其轉(zhuǎn)化為數(shù)字并返回
c. 否則拋出類型錯誤

var o = {};
o.valueOf = function () {
return 233;
};
Number(o);    // 233

(2)比較和運算
在執(zhí)行 “>”顶滩、“<”余掖、“+”、“-” 等操作的時候诲祸,如果涉及到引用類型數(shù)據(jù)浊吏,大部分引用類型數(shù)據(jù)在運算之前,會先嘗試執(zhí)行其 valueOf 方法救氯,如果該方法返回了一個基本數(shù)據(jù)類型,則拿該返回值替代對象本身參與運算否則則嘗試執(zhí)行 toString 方法歌憨,如果該方法返回了一個基本類型數(shù)據(jù)着憨,則使用該數(shù)據(jù)參與操作;如果該方法返回的不是基本類型數(shù)據(jù)务嫡,則嘗試執(zhí)行 valueOf 方法甲抖,如果該方法返回了一個基本類型數(shù)據(jù),則使用該數(shù)據(jù)參與操作心铃;否則將提示 TypeError准谚。

var o = {};
o.toString = function () {
    return 2;
}

// 此時還沒有為 o 添加 valueOf 方法
// 它將先調(diào)用繼承自 Object.prototype.valueOf 方法
// 返回值是它自身
// 于是則調(diào)用這里我們?yōu)閷嵗砑拥?toString 方法
o == 2;        // true

// 這里為實例添加了 valueOf 方法
// 一開始,它就將調(diào)用我們?yōu)閷嵗砑拥?valueOf 方法
// 返回值 1 是基本類型數(shù)據(jù)
// 則再調(diào)用 toString 方法
o.valueOf = function () {
    return 1;
}
o == 1;    // true
o + 1;      // 2
o * 5;       // 5

注意前面說的是“大部分引用類型數(shù)據(jù)”去扣,唯一不遵循此規(guī)則的是 Date 類型對象柱衔。與其它引用類型數(shù)據(jù)不同的是,在比較或者計算的時候,它會先嘗試調(diào)用其 toString 方法唆铐,如果沒有返回基本數(shù)據(jù)類型才嘗試調(diào)用其 valueOf 方法哲戚。

var t = new Date();

// t 繼承自 Date.prototype 上的 toString / valueOf 都能返回基本類型數(shù)據(jù) 
t.valueOf();      // 返回時間戳,如 1505438878370
t.toString();      // 時間信息字符串艾岂,如 "Fri Sep 15 2017 09:27:58 GMT+0800 (CST)"

t + 2344444;   // 并不會得到一個時間戳顺少,而是 "Fri Sep 15 2017 09:27:58 GMT+0800 (CST)2344444"

所以當(dāng)你不清楚它會得到什么值的時候,請自己調(diào)用 toString / valueOf 方法王浴,后來 Date.prototype 對象上增加了一個 getTime 方法替代 valueOf 獲取時間戳脆炎,但是這個方法在 IE 存在兼容性問題,僅 IE9+ 有效氓辣。

四秒裕、再轉(zhuǎn)換

到這里,其實就很清晰了筛婉。

[] == false;      // true

其實就是:

([]).toString() == false;    // true

也就是:

'' == false;    // true

這里就涉及了基本類型數(shù)據(jù)的隱式轉(zhuǎn)換問題了簇爆。基本依照以下規(guī)則:

  • 兩個都是數(shù)值爽撒,則比較數(shù)值
  • 兩個都是字符串入蛆,則比較字符編碼值
  • 其中一個是數(shù)值,則要把另個轉(zhuǎn)化成數(shù)值進行比較
  • 如果其中一個是對象硕勿,則調(diào)用 valueOf / toString 方法
  • 如果有一個是布爾值哨毁,則將其轉(zhuǎn)化成數(shù)值

顯然這里滿足最后一條規(guī)則,比較的時候源武,其實將會嘗試將二者轉(zhuǎn)化為數(shù)字類型扼褪。相當(dāng)于:

Number('') == Number(false);      // true

即:

0 == 0;    // true

五、總結(jié)

JavaScript 是一門弱類型語言粱栖,但是弱類型并不代表沒有類型话浇,相反的是,JavaScript 是一門類型豐富的語言闹究,除了常見語言的數(shù)字幔崖、字符串、布爾渣淤、對象赏寇、函數(shù)、null 等价认,更是有一個神奇的 undefined 類型嗅定。一邊是弱類型,一邊又是多種類型用踩,這看似矛盾渠退,但由于隱式類型轉(zhuǎn)換的存在忙迁,這種矛盾看起來又如此的合理。
P.S. 雖然上面的代碼中智什,我使用了大量的 “==”动漾,而非 “===”,但這僅是學(xué)習(xí)用的荠锭。實際開發(fā)的時候旱眯,我也推薦使用 “===”。
一方面证九,如果由于自己的疏忽删豺,沒能正確處理好隱式類型轉(zhuǎn)換,往往會造成意料之外的問題愧怜,為項目帶來潛在的風(fēng)險呀页,比如我想驗證某個變量是否是 undefined,如果采用:

value == undefined; 

但實際上拥坛,null 也會被匹配進來蓬蝶,可能造成潛在的風(fēng)險,如果使用 “===” 就不會有這個問題猜惋;

另一方面丸氛,如果多人協(xié)作開發(fā),隱式類型轉(zhuǎn)換往往會為其他人帶來困擾著摔,尤其是在成員間開發(fā)能力參差不齊的情況下缓窜。
例如,我想驗證一個值是否是布爾值 true谍咆,但是我寫了這樣的代碼:

value == true;

你知道哪些數(shù)據(jù)會匹配成功么禾锤?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市摹察,隨后出現(xiàn)的幾起案子恩掷,更是在濱河造成了極大的恐慌,老刑警劉巖供嚎,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件螃成,死亡現(xiàn)場離奇詭異,居然都是意外死亡查坪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門宁炫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來偿曙,“玉大人,你說我怎么就攤上這事羔巢⊥洌” “怎么了罩阵?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長启摄。 經(jīng)常有香客問我稿壁,道長,這世上最難降的妖魔是什么歉备? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任傅是,我火速辦了婚禮,結(jié)果婚禮上蕾羊,老公的妹妹穿的比我還像新娘喧笔。我一直安慰自己,他們只是感情好龟再,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布书闸。 她就那樣靜靜地躺著,像睡著了一般利凑。 火紅的嫁衣襯著肌膚如雪浆劲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天哀澈,我揣著相機與錄音牌借,去河邊找鬼。 笑死日丹,一個胖子當(dāng)著我的面吹牛走哺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播哲虾,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丙躏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了束凑?” 一聲冷哼從身側(cè)響起晒旅,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎汪诉,沒想到半個月后废恋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡扒寄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年鱼鼓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片该编。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡迄本,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出课竣,到底是詐尸還是另有隱情嘉赎,我是刑警寧澤置媳,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站公条,受9級特大地震影響拇囊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜靶橱,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一寥袭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抓韩,春花似錦纠永、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至英上,卻和暖如春炭序,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苍日。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工惭聂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人相恃。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓辜纲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拦耐。 傳聞我的和親對象是個殘疾皇子耕腾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,237評論 0 4
  • 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的 JavaScript 類型 使用基本類型和基本包裝類型 引用類型的...
    悶油瓶小張閱讀 681評論 0 0
  • 六月時候,這十分炎熱的季節(jié)杀糯,在其上旬的某天扫俺,心中突生想法,要去東輝峽谷走一遭固翰。我選擇了單車出行狼纬,做此決定,...
    楊旭東_97e5閱讀 405評論 0 2
  • 正月十六月兒明骂际, 男女老少烤雜病疗琉。 烤烤腳,百病消歉铝, 烤烤腚没炒,一年四季不得病。 …… 正月十六家鄉(xiāng)有“烤雜病”的習(xí)...
    女派閱讀 1,520評論 0 0
  • 今天開始學(xué)習(xí)英語單詞
    encome閱讀 243評論 0 0