JS加法運(yùn)算全解析

終極命題:
在JS中:[]+[]、[]+{}、{}+[]、{}+{}的結(jié)果分別是什么?

一、JS中的類型
  • 基本類型
    JS的基本類型包括Undefined、Null、Boolean、Number和String五種。Undefined類型和Null類型的都只有一個(gè)值,即undefined和null;Boolean類型有兩個(gè)值:true和false;Number類型的值有很多很多;String類型的值理論上有無數(shù)個(gè)。
  • 值類型
    JS中的值有原始類型(Primitive)和對象類型(Object)。在做相加等操作時(shí),不是原始類型的要先轉(zhuǎn)換為原始類型,再執(zhí)行相關(guān)的操作。
二、JS中的加法運(yùn)算

1、使用ToPrimitive運(yùn)算轉(zhuǎn)換左右運(yùn)算元為原始數(shù)據(jù)類型(primitive)。
2、在轉(zhuǎn)換后,如果其中一個(gè)運(yùn)算元出現(xiàn)原始數(shù)據(jù)類型是“字符串”類型值時(shí),則另一運(yùn)算元強(qiáng)制轉(zhuǎn)換為字符串,然后做字符串的連接運(yùn)算。
3、在其他情況時(shí),所有運(yùn)算元都會(huì)轉(zhuǎn)換為原始數(shù)據(jù)類型的“數(shù)字”類型值,然后作數(shù)字的相加。

三、ToPrimitive內(nèi)部運(yùn)算

加號運(yùn)算符只能用于原始數(shù)據(jù)類型,對于對象類型的值,需要進(jìn)行數(shù)據(jù)轉(zhuǎn)換。在ECMAScript中,有一個(gè)抽象的ToPrimitive運(yùn)算,用于將對象轉(zhuǎn)換為原始數(shù)據(jù)類型,在對象的加法、關(guān)系比較或值相等比較的運(yùn)算中,都會(huì)用到。

關(guān)于ToPrimitive的說明語法:

ToPrimitive(input, PreferredType?)

input代表代入的值,PreferredType可以是數(shù)字(Number)或字符串(String)其中一種,表示需要優(yōu)先轉(zhuǎn)換的原始類型。但如果沒有提供這個(gè)值也就是預(yù)設(shè)情況,則會(huì)設(shè)置轉(zhuǎn)換的hint值為"default"。這個(gè)首選的轉(zhuǎn)換原始類型的指示(hint值),是在作內(nèi)部轉(zhuǎn)換時(shí)由JS視情況自動(dòng)加上的,一般情況就是預(yù)設(shè)值。

而在JS的Object原型的設(shè)計(jì)中,有兩個(gè)方法,valueOf與toString,在對象的數(shù)據(jù)類型轉(zhuǎn)換過程中會(huì)根據(jù)傳入的值調(diào)整被調(diào)用的順序。

  • PreferredType為數(shù)字(Number)時(shí)
    當(dāng)PreferredType為數(shù)字(Number)時(shí),input為要被轉(zhuǎn)換的值,轉(zhuǎn)換input值的步驟:
    1.如果input為原始數(shù)據(jù)類型,直接返回input。
    2.否則,input是對象,調(diào)用valueOf()方法,如果能得到原始數(shù)據(jù)類型的值,返回這個(gè)值。
    3.否則,input是對象,調(diào)用toString()方法,如果能得到原始數(shù)據(jù)類型的值,返回這個(gè)值。
    4.否則,拋出TypeError錯(cuò)誤。

  • PreferredType為字符串(String)時(shí)
    1.如果input是原始數(shù)據(jù)類型,直接返回input。
    2.否則,input是對象,調(diào)用toString()方法,如果能得到原始數(shù)據(jù)類型的值,返回這個(gè)值。
    3.否則,input是對象,調(diào)用valueOf()方法,如果能得到原始數(shù)據(jù)類型的值,返回這個(gè)值。
    4.否則,拋出TypeError錯(cuò)誤。

  • PreferredType未提供(default)時(shí)
    PreferredType預(yù)設(shè)類型為Number,所以先調(diào)用valueOf(),再調(diào)用toString()。

其中比較特殊的是Date對象和Symbol對象,他們覆蓋了原來的PreferredType行為,Date對象預(yù)設(shè)首選類型是String。

四、valueOf()和toString()

valueOf()和toString()是Object上的兩個(gè)方法,但是在JS中,可能會(huì)根據(jù)Object之間的差異,返回值有所不同。

  • 普通的Object對象
    valueOf():返回對象本身。
    toString():"[object Object]"字符串值,不同的內(nèi)建對象的返回值是"[object type]"字符串,"type"指的是對象本身的類型識別,例如Math對象是返回"[object Math]"字符串。但有些內(nèi)建對象因?yàn)楦采w了這個(gè)方法,所以直接調(diào)用時(shí)不是這種值。(注意: 這個(gè)返回字符串的前面的"object"開頭英文是小寫,后面開頭英文是大寫)。

利用Object中的toString來進(jìn)行各種不同對象的判斷語法:

Object.prototype.toString.call([])
"[object Array]"

Object.prototype.toString.call(new Date)
"[object Date]"

需要配合call,才能得到正確的對象類型值。

  • Array(數(shù)組)
    Array(數(shù)組)雖然是個(gè)對象類型,但它與Object的設(shè)計(jì)不同,它的toString有覆蓋,說明一下數(shù)組的valueOf與toString的兩個(gè)方法的返回值:
    valueOf(): 返回對象本身
    toString(): 相當(dāng)于用數(shù)組值調(diào)用join(',')所返回的字符串。也就是[1,2,3].toString()會(huì)是"1,2,3",這點(diǎn)要特別注意。

  • Function對象
    Function對象很少會(huì)用到,它的toString也有被覆蓋,所以并不是Object中的那個(gè)toString,F(xiàn)unction對象的valueOf與toString的兩個(gè)方法的返回值:
    valueOf(): 返回對象本身
    toString(): 返回函數(shù)中包含的代碼轉(zhuǎn)為字符串值。

  • Date對象
    valueOf(): 返回給定的時(shí)間轉(zhuǎn)為UNIX時(shí)間(自1 January 1970 00:00:00 UTC起算),但是以微秒計(jì)算的數(shù)字值
    toString(): 返回本地化的時(shí)間字符串

五、Number、String、Boolean包裝對象

JS的包裝對象是必須使用new關(guān)鍵字進(jìn)行對象實(shí)例化的,直接使用Number()、String()與Boolean()三個(gè)強(qiáng)制轉(zhuǎn)換函數(shù)的用法。例如new Number(123),而Number('123')則是強(qiáng)制轉(zhuǎn)換其他類型為數(shù)字類型的函數(shù)。

  • 包裝對象
    包裝對象是JS為原始數(shù)據(jù)類型數(shù)字、字符串、布爾專門設(shè)計(jì)的對象,包裝對象的valueOf與toString的兩個(gè)方法在原型上有經(jīng)過覆蓋,所以它們的返回值與一般的 Object的設(shè)計(jì)不同:
    valueOf方法返回值: 對應(yīng)的原始數(shù)據(jù)類型值
    toString方法返回值: 對應(yīng)的原始數(shù)據(jù)類型值,轉(zhuǎn)換為字符串類型時(shí)的字符串值
    toString方法會(huì)比較特別,這三個(gè)包裝對象里的toString的細(xì)部說明如下:
    1、Number包裝對象的toString方法: 可以有一個(gè)傳參,可以決定轉(zhuǎn)換為字符串時(shí)的進(jìn)位(2/8/16)
    2、 String包裝對象的toString方法: 與String包裝對象中的valueOf相同返回結(jié)果
    3、Boolean包裝對象的toString方法: 返回"true"或"false"字符串

  • 強(qiáng)制轉(zhuǎn)換
    Number()、String()與Boolean()三個(gè)強(qiáng)制轉(zhuǎn)換函數(shù),所對應(yīng)的就是在ECMAScript標(biāo)準(zhǔn)中的ToNumber、ToString、ToBoolean三個(gè)內(nèi)部運(yùn)算轉(zhuǎn)換的對照表。

    通過ToNumber()把值轉(zhuǎn)換成Number:

    參數(shù) 結(jié)果
    undefined NaN
    null +0
    boolean true被轉(zhuǎn)換為1,false轉(zhuǎn)換為+0
    number 無需轉(zhuǎn)換
    string 由字符串解析為數(shù)字。例如,”324″被轉(zhuǎn)換為324

    通過ToString()把值轉(zhuǎn)化成字符串:

    參數(shù) 結(jié)果
    undefined “undefined”
    null “null”
    boolean “true” 或者 “false”
    number 數(shù)字作為字符串。比如,”1.765″
    string 無需轉(zhuǎn)換
六、從實(shí)例中理解
  • 運(yùn)算元其一為字符串(String)
/**
 * 運(yùn)算元其一為字符串(String)
 */
console.log('12'+1);            // 121
console.log('abc'+'def');       // abcdef
console.log('1'+true);          //1true
console.log('1'+undefined);     //1undefined
console.log('1'+null);          //1null

運(yùn)算元其一為字符串,字符串的拼接運(yùn)算。

  • 運(yùn)算元其一為數(shù)字(Number)
/**
 * 運(yùn)算元其一為數(shù)字(Number)
 */
console.log(1+1);            // 2
console.log(1+'def');       // 1def
console.log(1+true);          //2
console.log(1+undefined);     //NaN
console.log(1+null);          //1

1+'def'為運(yùn)算元其一為字符串情況,其余為在沒有字符串情況下,運(yùn)算元其一為數(shù)字,做類型轉(zhuǎn)換后相加。

  • 數(shù)字(Number)/字符串(String)以外的原始類型相加
/**
 * 數(shù)字(Number)/字符串(String)以外的原始類型相加
 */
console.log(true+true);             // 2
console.log(true+null);             // 1
console.log(true+undefined);        //NaN
console.log(undefined+null);        //NaN
console.log(undefined+undefined);   //NaN
console.log(null+null);            //0

當(dāng)數(shù)字與字符串以外的,其他原始數(shù)據(jù)類型直接使用加號運(yùn)算時(shí),就是轉(zhuǎn)為數(shù)字再運(yùn)算,這與字符串完全無關(guān)。

  • 空數(shù)組 + 空數(shù)組
console.log([] + []);        //""

兩個(gè)數(shù)組相加,左右運(yùn)算元先調(diào)用valueOf(),返回?cái)?shù)組本身,調(diào)用toString(),返回原始數(shù)據(jù)類型,即空字符串,作連接操作,得到一個(gè)空字符串。

  • 空對象 + 空對象
console.log({} + {});        //"[object Object][object Object]"

兩個(gè)對象相加,左右運(yùn)算元先調(diào)用valueOf(),返回對象本身,調(diào)用toString(),返回原始數(shù)據(jù)類型,即對象字符串[object Object],作連接操作,得到字符串[object Object][object Object]

console.log({}+{})得到的這樣的結(jié)果,但是,在有些瀏覽器例如Firefox、Edge控制臺直接輸入{}+{},會(huì)得到NaN,因?yàn)闉g覽器會(huì)把{} + {}直譯為相當(dāng)于+{}語句,因?yàn)樗鼈儠?huì)認(rèn)為以花括號開頭({)的,是一個(gè)區(qū)塊語句的開頭,而不是一個(gè)對象字面量,所以會(huì)認(rèn)為略過第一個(gè){},把整個(gè)語句認(rèn)為是個(gè)+{}的語句,也就是相當(dāng)于強(qiáng)制求出數(shù)字值的Number({})函數(shù)調(diào)用運(yùn)算,相當(dāng)于Number("[object Object]")運(yùn)算,最后得出的是NaN。如果加上圓括號({}) + {},則可以避免這樣的問題。

  • 空對象 + 空數(shù)組
console.log({} + []);        //"[object Object]"

空對象和空數(shù)組相加,左右運(yùn)算元先調(diào)用valueOf(),返回對象數(shù)組本身,調(diào)用toString(),返回原始數(shù)據(jù)類型,即對象字符串[object Object]和"",作連接操作,得到字符串[object Object]

直接console.log得到的都是一樣的值,但是直接在瀏覽器控制臺輸入:

> {} + []
0

> [] + {}
"[object Object]"

{} + []相當(dāng)于+[]語句,也就是相當(dāng)于強(qiáng)制求出數(shù)字值的Number([])運(yùn)算,相當(dāng)于Number("")運(yùn)算,最后得出的是0數(shù)字。

  • Date對象
console.log(1 + (new Date()));       //"1Tue Aug 14 2018 21:18:24 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間)"

Date對象首選類型String,先調(diào)用toString(),得到字符串做字符串連接運(yùn)算。

要得出Date對象中的valueOf返回值,需要使用一元加號(+),來強(qiáng)制轉(zhuǎn)換它為數(shù)字類型,例如以下的代碼:

console.log(+new Date());
1534298171747
  • Symbols類型
    ES6中新加入的Symbols數(shù)據(jù)類型,它不算是一般的值也不是對象,它并沒有內(nèi)部自動(dòng)轉(zhuǎn)型的設(shè)計(jì),所以完全不能直接用于加法運(yùn)算,使用時(shí)會(huì)報(bào)錯(cuò)。

  • +[]/+{}

console.log(+[]);     // 0
console.log(+{});     // NaN
console.log(+null);     //0
console.log(+true);     //1
console.log(+undefined);     //NaN

一元加號運(yùn)算時(shí),唯一的運(yùn)算元相當(dāng)于強(qiáng)制求出數(shù)字值的Number([])運(yùn)算。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,504評論 0 13
  • ??引用類型的值(對象)是引用類型的一個(gè)實(shí)例。 ??在 ECMAscript 中,引用類型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)...
    霜天曉閱讀 1,213評論 0 1
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,665評論 0 4
  • 運(yùn)算符是處理數(shù)據(jù)的基本方法,用來從現(xiàn)有的值得到新的值。JavaScript 提供了多種運(yùn)算符,本章逐一介紹這些運(yùn)算...
    許先生__閱讀 697評論 0 3
  • 最近在看《白鹿原》,看著這發(fā)生在白鹿原上的故事,尤其關(guān)于白家長工鹿三和黑娃斗地主的那段,不由得讓我想起了我的地主外...
    夏花de解憂雜貨鋪閱讀 2,363評論 0 9

友情鏈接更多精彩內(nèi)容