33 個 js 核心概念(四):顯式 (名義) 與 隱式 (鴨子)類型轉(zhuǎn)換

原文地址:阿木木的博客

顯式與隱式類型轉(zhuǎn)換

前言

說實話,JavaScript 的類型轉(zhuǎn)換是個相當(dāng)頭疼的問題蜜另,無論是對于初學(xué)者還是有經(jīng)驗的老司機适室。它的難處并不在于概念多難理解,而是情況多且雜举瑰,看似相同的情況結(jié)果卻又出人意料捣辆,很少有人能保證時刻都能做出正確的判斷。

因此此迅,這篇文章希望能講的足夠細致和明確汽畴,讓大家能夠在日常使用中,能夠盡快的搞清楚類型轉(zhuǎn)換的順序和結(jié)果耸序。

長文預(yù)警忍些,建議先 mark, 分多次查看。

一坎怪、類型轉(zhuǎn)換

1. 什么叫類型轉(zhuǎn)換罢坝?

我們知道,JavaScript 中存在七種數(shù)據(jù)類型搅窿,在必要的時候炸客,我們會對不同類型的值進行相互間的轉(zhuǎn)換。比如說戈钢,在進行條件判斷時痹仙,我們需要將其他類型的值轉(zhuǎn)為布爾類型值,在使用 console.log() 打印內(nèi)容時殉了,需要將其轉(zhuǎn)為字符串輸出开仰。

2. JavaScript 中的類型轉(zhuǎn)換方式有哪些?

在 JavaScript 中,分為顯式類型轉(zhuǎn)換和隱式類型轉(zhuǎn)換众弓。

其中恩溅,顯式類型轉(zhuǎn)換是我們?yōu)榱斯δ苄枰藶榈膶⒁环N類型的值轉(zhuǎn)換為另一中類型谓娃,轉(zhuǎn)換的時機和結(jié)果都是我們預(yù)期的脚乡;而隱式類型轉(zhuǎn)換則是 JavaScript 在代碼運行時,未經(jīng)我們允許而進行的強制類型轉(zhuǎn)換滨达。

二奶稠、顯式類型轉(zhuǎn)換

1. 其他類型轉(zhuǎn)換為字符串

值類型 例子 轉(zhuǎn)換后 調(diào)用法則
number 34 '34' String(34)
boolean true 'true' String(true)
boolean false 'false' String(false)
undefiend undefined 'undefined' String(undefined)
null null 'null' String(null)
object { a: 'fa' } "[object Object]" String({a: 'fa'})
object new String(45) '45' String(new String(45))
object [1, 2] '1,2' String([1,2])
object function() {var d;} "function() { var d; }" String(function() {var d;})

其他類型的值轉(zhuǎn)換為字符串,是通過調(diào)用原生函數(shù)String()實現(xiàn)捡遍,但不同類型值的實現(xiàn)卻有明顯的差異锌订。

對于基本類型的值,直接將其轉(zhuǎn)化為值的字符串形式画株。而對于對象類型來說辆飘,便有些復(fù)雜了。

首先谓传,每個對象內(nèi)部都有一個 [[Class]] 屬性蜈项,我們通過Object.prototype.toString() 方法可以得到這個屬性的字符串值。

對于對象(如{ a: 'ff'; })而言续挟,除非自己定義 toString() 方法战得,否則,調(diào)用 String() 方法將返回和調(diào)用 Object.prototype.toString() 相同的值庸推。(如 : "[object Object]")。

const obj_1 = {
  b: 'lalala'
};
const obj_2 = {
  toString() {
    return "fasfa";
  }
};
String(obj_1); // '[object Object]'
String(obj_2); // 'fasfa'

其次浇冰, JavaScript 中贬媒,除了普通對象,還有以下幾種:

  1. 封裝對象

    對于基本類型值 string肘习、number际乘、boolean 是沒有 .lengthtoString() 方法的,因此漂佩,JavaScript 提供了內(nèi)建函數(shù) String()脖含、Number()Boolean() 投蝉,通過 new 調(diào)用后會將基本類型值封裝為一個對象养葵。

    如果想要取到封裝對象中的基本類型值,可以使用 valueOf() 方法瘩缆。

    // string 類型
    const a = 'i am string';
    typeof a; //  'string'
    // string 封裝對象
    const b = new String('i am sringObject');
    typeof b; // 'object'
    // 拆封
    b.valueOf(); // i am sringObject
    

    那對于封裝對象关拒,String() 會返回什么值呢?

    事實上,封裝對象對于 toString() 方法進行了封裝着绊,因此谐算,對封裝對象調(diào)用 String() 方法,將會返回封裝對象調(diào)用toString() 方法返回的值归露。

    const numObj = new Number(false); // Number {0}
    numObj.toString(); // '0'
    String(numObj); // '0'
    
  2. 函數(shù)

    對于函數(shù)來說洲脂,它也包裝了自己的 toString()方法,因此剧包,調(diào)用 String() 方法時將返回函數(shù)字符串化后的值恐锦。

    function bar() {
      console.log('bar');
    }
    String(bar); // "function bar() {?  console.log('bar');?}"
    bar.toString(); // "function bar() {?  console.log('bar');?}"
    Object.prototype.toString.call(bar); // "[object Function]"
    

    從上例可以看到,String()toString() 方法調(diào)用的是函數(shù)自己封裝的toSring()玄捕,如果調(diào)用對象的 toString() 方法踩蔚,則函數(shù)與普通對象一樣,返回的是函數(shù)對象內(nèi)部的 [[Class]] 屬性枚粘。

  3. 數(shù)組

    數(shù)組同函數(shù)一樣馅闽,同樣包裝了自己的 toString() 方法。此方法會將數(shù)組中的每一項用逗號連接成一個字符串馍迄。

    const arr = [1,4,6];
    String(arr); // "1,4,6"
    arr.toString(); // "1,4,6"
    Object.prototype.toString.call(arr); // "[object Array]"
    

2. 其他類型值轉(zhuǎn)為數(shù)字

同樣福也,先感受一下什么叫絕望?~~

值類型 例子 轉(zhuǎn)換后 調(diào)用法則
string '34' 34 Number('34')
string '' 0 Number('')
string '34fad' NaN Number('34fad')
string '34fad'攀圈、'34.24'暴凑、'34' 34 parseInt('34fad')
string '34fad'、'34' 34 parseFloat(值)
string '34.34' 34.34 parseFloat(值)
boolean true 1 Number(true)
boolean false 0 Number(false)
undefiend undefined NaN Number(undefined)
null null 0 Number(null)
object { a: 'fa' } NaN Number({a: 'fa'})
object new String('fff') NaN Number(new String('fff'))
object [] 0 Number([])
object [1, 2] NaN Number([1,2])
object function() {var d;} NaN Number(function() {var d;})

看完一臉懵逼有沒有赘来?现喳!哈哈,不用害怕犬辰,乍看上去嗦篱,大概會覺得異常混亂幌缝,其實稍加整理灸促,不外乎以下幾種情況:

  1. 轉(zhuǎn)換后值為 NaN

    數(shù)字與字符串不同,并不是任何類型值都能轉(zhuǎn)為數(shù)字涵卵,因此浴栽,就會有 NaN,意思就是 not a number轿偎。

    諸如包含非數(shù)字的字符串典鸡、undefined、非空數(shù)組坏晦,部分對象椿每,都是我們知道無法轉(zhuǎn)化為一個數(shù)字的伊者。

  2. boolean 類型值

    對于 truefalsetrue 轉(zhuǎn)換為 1间护,false 轉(zhuǎn)為 0亦渗。

  3. 帶有數(shù)字的字符串

    從上面我們可以看到,對于帶有數(shù)字的字符串汁尺,有三種方法進行轉(zhuǎn)換法精,但規(guī)則不同。

    • Number() 方法會對字符串整體進行轉(zhuǎn)換痴突, 它會先判斷這個字符串是否是個正確的數(shù)字字符串搂蜓,如果不是,則會返回 NaN辽装。

    • parseInt() 方法則會對字符串從左往右依次解析帮碰,直到遇到第一個非數(shù)字字符(包括小數(shù)點),如果最左邊的字符是非數(shù)字字符拾积,則返回 NaN殉挽。

    • parseFloat() 方法解析順序同 parseInt() 相同,不同的是它遇到第一個小數(shù)點時會正常往右繼續(xù)解析拓巧,直至遇到非數(shù)字字符停止斯碌。

    其實嚴(yán)格來講,只有 Number() 方法是進行轉(zhuǎn)換操作肛度,而后兩者屬于將字符串解析 為數(shù)字傻唾,但為了講解方便,我將它們放在一起講述承耿。

  4. 對象

    對于對象而言冠骄,會先將對象轉(zhuǎn)為基本類型值,再對基本類型值調(diào)用 Number() 方法加袋。

    那如何將對象轉(zhuǎn)為基本類型值凛辣?首先會調(diào)用對象的 valueOf() 方法,如果沒有此方法或者此方法返回值不是基本類型值锁荔,則會調(diào)用toString() 方法,如果 toString() 方法不存在或者返回值也不是基本類型值蝙砌,會產(chǎn)生 TypeError 錯誤阳堕。

    // 普通對象
    const nomalObj = {
      a: '56'
    };
    nomalObj .valueOf(); // { a: '56'}
    nomalObj.toString(); // "[object Object]"
    // Number(nomalObj) 相當(dāng)于Number("[object Object]") 
    Number("[object Object]"); // NaN
    Number(nomalObj); // NaN
    
    // valueOf() 返回基本類型值的對象
    const obj_1 = {
      a: '56',
      valueOf: function() {
        return '23';
      }  
    };
    obj_1.valueOf(); // '23'
    // Number(obj_1) 相當(dāng)于 Number('23');
    Number('23'); // 23
    Number(obj_1); // 23
    
    // valueOf() 返回非基本類型值,toString() 返回基本類型值的對象
    const obj_2 = {
      a: '56',
      valueOf: function() {
        return {b: 34}
      },
      toString: function() {
        return false;
      }
    };
    obj_2.valueOf(); // {b: 34}
    obj_2.toString(); // false
    // Number(obj_2) 相當(dāng)于 Number(false)
    Number(obj_2); // 0
    Number(false); // 0
    

    上面的規(guī)則择克,適用于我們所說的所有對象恬总,比如數(shù)組,封裝對象和函數(shù)肚邢。

3. 其他類型轉(zhuǎn)換為 boolean 值

我們可以通過 Boolean()方法 或!!運算符來顯式的將一個值轉(zhuǎn)換為布爾值壹堰。

相對來說拭卿,判斷一個值是 true 還是 false 則比較容易,我們只需要記住以下幾種值會轉(zhuǎn)換為 false贱纠,而其他值峻厚,均為 true

  • undefined
  • null
  • false
  • +0谆焊、-0 和 NaN
  • ""

當(dāng)我們看到 []惠桃、{} 甚至是 "''" 時,也一定要記住辖试,它們是真值辜王。


Boolean(false); // fasle
Boolean([]); //true
Boolean({}); //true
Boolean(''); // false
Boolean('""'); // true
Boolean('false'); // true

三、隱式強制類型轉(zhuǎn)換

除了進行強制類型轉(zhuǎn)換罐孝,JavaScript 會在運行時根據(jù)需要呐馆,自動進行類型的轉(zhuǎn)換,盡管這個特點飽受爭議莲兢,但不得不承認(rèn)汹来,某些情況下我們?nèi)耘f更喜歡使用某些隱式轉(zhuǎn)換規(guī)則。

一旦某些隱式的規(guī)則被接受并廣泛使用怒见,從某種意義上來講俗慈,這些規(guī)則便同顯式轉(zhuǎn)換一樣。

1. 奇怪的 +

先看一一個最常見的例子:

const a = 5;
const b = '6';
console.log(a+a); // 10
console.log(a+b); // '56'
console.log(b+b); // '66'

之所以會產(chǎn)生上例中的狀況遣耍,原因就在于在JavaScript 中闺阱,+ 運算符既可以作用于number 類型值,也可以作用于 string 類型值舵变。前者進行數(shù)字相加酣溃,后者則進行字符串的拼接。

這就是為什么5 + 5 = 10'6' + '6' = '66'纪隙。而當(dāng) + 號兩邊既有數(shù)字也有字符串時赊豌,則會隱式的將數(shù)字轉(zhuǎn)換為字符串,然后進行字符串的拼接绵咱。

那兩邊沒有字符串的情況呢碘饼?比如:

const a = [1,4];
const b = [2,3];
const c = 4;
console.log(a+c); // '1,44'
console.log(a+b); // '1,42,3'

為什么會這樣?原來只要+ 的其中一個操作數(shù)可以通過某種方式(toPrimitive)轉(zhuǎn)換為字符串悲伶,就會進行字符串的拼接艾恼。

我們知道,數(shù)組[1,4] 可以通過 toString() 方法返回字符串 '1,4'麸锉,因此钠绍,[1,4] + 4 就相當(dāng)于 '1,4' + 4

因為這個特性花沉,我們在想將一個數(shù)字 a 轉(zhuǎn)換為字符串時柳爽,便可以直接使用 a + '' 的形式即可媳握。相對于顯式使用String(a),隱式轉(zhuǎn)換則更加簡潔磷脯。

從數(shù)組的例子我們可以看到蛾找,除了數(shù)字,其他類型的值也可以通過 + ' ' 的形式轉(zhuǎn)化為字符串争拐。

const a = {b: '2'}
console.log( a+ ''); // "[object Object]"

但有一點需要注意腋粥,對于對象而言,使用 String() 方法是直接取這個對象 toString() 方法的返回值架曹,而 + ' ' ,則會對這個對象調(diào)用 valueOf 反法隘冲,然后對 valueOf 的返回值調(diào)用 toString(),將其轉(zhuǎn)換為字符串绑雄。

const a = {
  toString: function() { return 45 },
  valueOf: function() { return 4}
 };
String(a); // '45'
a + ' '; //  // '4'

好在除非我們特意去改變一個對象的 valueOf 及 'toString()' 方法展辞,通過上述兩個方式的轉(zhuǎn)換后的結(jié)果都是一致的。

2. 有用的 -

+ 號不同的是万牺,- 號只能用于數(shù)字的相減罗珍,對于它兩邊的操作數(shù),都會經(jīng)過隱式類型轉(zhuǎn)換轉(zhuǎn)為數(shù)字脚粟。

const a = '34';
const b = '4';
console.log(a - b); // 30
const c = 'dd';
console.log(a - c); // NaN
const d = [4];
console.log(a - d); // 30

根據(jù)上例覆旱,我們可看到,如果 - 號兩邊是字符串核无,則會將他們強制轉(zhuǎn)換為數(shù)字扣唱,如果 - 兩邊不是字符串,則會先將其轉(zhuǎn)為字符串团南,再將這個字符串轉(zhuǎn)為數(shù)字噪沙。

3. 隱式轉(zhuǎn)換為布爾值

將其他類型值隱式轉(zhuǎn)換為布爾值是我們最常用的一種轉(zhuǎn)換。因為程序的編寫實質(zhì)上就是不停的進行判斷吐根。

在以下場景中正歼,都是進行判斷,而只要傳入的值不是布爾值拷橘,都會通過隱式類型轉(zhuǎn)換轉(zhuǎn)為布爾值局义。

  1. if (..) {} 語句中的條件判斷表達式。
  2. for ( .. ; .. ; ..) 語句中的條件判斷表達式冗疮。
  3. while (..)do ... while ( ..) 中的條件判斷表達式萄唇。
  4. ? : 中的條件判斷表達式。
  5. 邏輯或 || 或邏輯與 && 左邊的操作數(shù)赌厅。

在這些情況下穷绵,都將會進行其他類型值到布爾類型值的隱式轉(zhuǎn)換轿塔,規(guī)則同顯式調(diào)用 Boolean()特愿。

上面就是不同數(shù)據(jù)類型直接顯式或隱式的轉(zhuǎn)換規(guī)則仲墨,我們不需要將每一種情況都牢記在心,但有必要對他們進行充分的了解揍障,這可以保證我們在實際寫代碼時避免不少奇怪又難以排查的 bug 目养。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市毒嫡,隨后出現(xiàn)的幾起案子癌蚁,更是在濱河造成了極大的恐慌,老刑警劉巖兜畸,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件努释,死亡現(xiàn)場離奇詭異,居然都是意外死亡咬摇,警方通過查閱死者的電腦和手機伐蒂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肛鹏,“玉大人逸邦,你說我怎么就攤上這事≡谌牛” “怎么了缕减?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長芒珠。 經(jīng)常有香客問我桥狡,道長,這世上最難降的妖魔是什么妓局? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任总放,我火速辦了婚禮,結(jié)果婚禮上好爬,老公的妹妹穿的比我還像新娘局雄。我一直安慰自己,他們只是感情好存炮,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布炬搭。 她就那樣靜靜地躺著,像睡著了一般穆桂。 火紅的嫁衣襯著肌膚如雪宫盔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天享完,我揣著相機與錄音灼芭,去河邊找鬼。 笑死般又,一個胖子當(dāng)著我的面吹牛彼绷,可吹牛的內(nèi)容都是我干的巍佑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼寄悯,長吁一口氣:“原來是場噩夢啊……” “哼萤衰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起猜旬,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤脆栋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后洒擦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體椿争,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年熟嫩,在試婚紗的時候發(fā)現(xiàn)自己被綠了丘薛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡邦危,死狀恐怖洋侨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倦蚪,我是刑警寧澤希坚,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站陵且,受9級特大地震影響裁僧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慕购,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一聊疲、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沪悲,春花似錦获洲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涉馁,卻和暖如春门岔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背烤送。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工寒随, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓妻往,卻偏偏與公主長得像逢防,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蒲讯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

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