摘要:這樣導(dǎo)致結(jié)果不一致,等解析語句為,對(duì)空對(duì)象強(qiáng)制轉(zhuǎn)為數(shù)字類型,即為,將非空字符串轉(zhuǎn)換為數(shù)字類型,結(jié)果為。綜上,右邊表達(dá)式轉(zhuǎn)換為。
首先從一系列讓JavaScript初學(xué)者抓狂的運(yùn)算說起。
1 + {} {} + 1 [] + {} {} + [] [] + [] {} + {}
能全部答對(duì)上面的運(yùn)算結(jié)果,不必浪費(fèi)時(shí)間繼續(xù)閱讀本文了。
如果對(duì)某一些的結(jié)果還不確定,請(qǐng)慢慢往下看。
上面列的所有運(yùn)算,需要理清以下兩點(diǎn):
+和{}的解析規(guī)則;
JavaScript是如何進(jìn)行類型轉(zhuǎn)換的;
+和{}的解析規(guī)則 +符號(hào)數(shù)字的加法運(yùn)算,二元運(yùn)算符
字符串的連接運(yùn)算,二元運(yùn)算符
正號(hào),一元運(yùn)算符,強(qiáng)制轉(zhuǎn)換其他類型的運(yùn)算元為數(shù)字類型
{}符號(hào)對(duì)象的字面量
區(qū)塊語句
加法運(yùn)算規(guī)則首先,我們來了解,+符號(hào)作為加號(hào)二元運(yùn)算符的運(yùn)算規(guī)則
使用ToPrimitive轉(zhuǎn)換左右運(yùn)算元為原始數(shù)據(jù)類型值;
在第1步轉(zhuǎn)換后,如果有運(yùn)算元出現(xiàn)原始數(shù)據(jù)類型為“字符串”類型時(shí),則另一運(yùn)算元強(qiáng)制轉(zhuǎn)換為字符串,然后做字符串連接運(yùn)算;
其他情況時(shí),所有運(yùn)算元都轉(zhuǎn)換為原始數(shù)據(jù)類型為“數(shù)字”類型,然后做數(shù)字相加運(yùn)算;
ToPrimitive運(yùn)算從上圖總結(jié)ToPrimitive運(yùn)算的語法說明:
ToPrimitive(input, PreferredType?)
input代表代入的值,而PreferredType可以是數(shù)字(Number)或字符串(String)其中一種,這會(huì)代表“優(yōu)先的”、“首選的”的要進(jìn)行轉(zhuǎn)換到哪一種原始類型,轉(zhuǎn)換的步驟會(huì)依這里的值而有所不同。
但如果沒有提供這個(gè)值也就是預(yù)設(shè)情況,則會(huì)設(shè)置轉(zhuǎn)換的hint值為default。這個(gè)首選的轉(zhuǎn)換原始類型的指示(hint值),是在作內(nèi)部轉(zhuǎn)換時(shí)由JS視情況自動(dòng)加上的,一般情況就是預(yù)設(shè)值。
轉(zhuǎn)換步驟為:
如果input是原始數(shù)據(jù)類型,則直接返回input。
否則,如果input是個(gè)對(duì)象時(shí),則調(diào)用對(duì)象的valueOf()方法,如果能得到原始數(shù)據(jù)類型的值,則返回這個(gè)值。
否則,如果input是個(gè)對(duì)象時(shí),調(diào)用對(duì)象的toString()方法,如果能得到原始數(shù)據(jù)類型的值,則返回這個(gè)值。
否則,拋出TypeError錯(cuò)誤。
上面的步驟2與3對(duì)調(diào),轉(zhuǎn)換步驟為:
如果input是原始數(shù)據(jù)類型,則直接返回input。
否則,如果input是個(gè)對(duì)象時(shí),則調(diào)用對(duì)象的toString方法,如果能得到原始數(shù)據(jù)類型的值,則返回這個(gè)值。
否則,如果input是個(gè)對(duì)象時(shí),調(diào)用對(duì)象的valueOf()方法,如果能得到原始數(shù)據(jù)類型的值,則返回這個(gè)值。
否則,拋出TypeError錯(cuò)誤。
有幾點(diǎn)值得注意:
數(shù)字是預(yù)設(shè)的PreferredType;
在一般情況下,加號(hào)運(yùn)算中的對(duì)象要作轉(zhuǎn)型時(shí),都是先調(diào)用valueOf再調(diào)用toString;
例外:Date 對(duì)象、Symbol 對(duì)象
Date 對(duì)象的預(yù)設(shè)首選類型是字符串(String);
Symbol 能顯示轉(zhuǎn)為字符串 String(Symbol) 和布爾值,不能轉(zhuǎn)為數(shù)字;
a + b: pa = ToPrimitive(a) pb = ToPrimitive(b) if(pa is string || pb is string) return concat(ToString(pa), ToString(pb)) else return add(ToNumber(pa), ToNumber(pb))
// ECMA-262, section 9.1, page 30. Use null/undefined for no hint, // (1) for number hint, and (2) for string hint. function ToPrimitive(x, hint) { if (!IS_SPEC_OBJECT(x)) return x; if (hint == NO_HINT) hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT; return (hint == NUMBER_HINT) ? DefaultNumber(x) : DefaultString(x); }
可以看出,Date類型的對(duì)象預(yù)設(shè)值為字符串(String)。
DefaultNumber和DefaultString方法
// ECMA-262, section 8.6.2.6, page 28. function DefaultString(x) { if (!IS_SYMBOL_WRAPPER(x)) { if (IS_SYMBOL(x)) throw MakeTypeError(kSymbolToString); var toString = x.toString; if (IS_SPEC_FUNCTION(toString)) { var s = % _CallFunction(x, toString); if (IsPrimitive(s)) return s; } var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf)) { var v = % _CallFunction(x, valueOf); if (IsPrimitive(v)) return v; } } throw MakeTypeError(kCannotConvertToPrimitive); }
// ECMA-262, section 8.6.2.6, page 28. function DefaultNumber(x) { var valueOf = x.valueOf; if (IS_SPEC_FUNCTION(valueOf)) { var v = % _CallFunction(x, valueOf); if (IS_SYMBOL(v)) throw MakeTypeError(kSymbolToNumber); if (IS_SIMD_VALUE(x)) throw MakeTypeError(kSimdToNumber); if (IsPrimitive(v)) return v; } var toString = x.toString; if (IS_SPEC_FUNCTION(toString)) { var s = % _CallFunction(x, toString); if (IsPrimitive(s)) return s; } throw MakeTypeError(kCannotConvertToPrimitive); }
至此,我們弄清楚了ToPrimitive內(nèi)部運(yùn)算規(guī)則。
原始數(shù)據(jù)類型轉(zhuǎn)換規(guī)則表對(duì)于原始數(shù)據(jù)類型直接的轉(zhuǎn)換規(guī)則就不一一解釋了,可以參看下表:
解析為數(shù)字1與空對(duì)象{}進(jìn)行加運(yùn)算,按照上面的規(guī)則,空對(duì)象{}按照valueOf -> toString順序,valueOf返回對(duì)象本身,所以調(diào)用toString返回"[object Object]"為字符串,根據(jù)規(guī)則,有一個(gè)為字符串,則進(jìn)行字符串連接運(yùn)算,數(shù)字1強(qiáng)制轉(zhuǎn)換為字符串"1",最終結(jié)果為"1[object Object]"。
{} + 1這個(gè)例子有坑,因?yàn)閷?duì)于瀏覽器引擎來說,它們會(huì)認(rèn)為以花括號(hào)開頭{的,是一個(gè) 區(qū)塊語句 的開頭,而不是一個(gè)對(duì)象字面量的語句,所以會(huì)認(rèn)為略過第一個(gè){}。所以這個(gè)例子相當(dāng)于+1,加號(hào)運(yùn)算會(huì)直接變?yōu)橐辉?hào)運(yùn)算,對(duì)數(shù)字1強(qiáng)制轉(zhuǎn)換為數(shù)字類型,結(jié)果為1。
[] + {}將[]和{}分別調(diào)用ToPrimitive方法,返回""和"[object Object]",然后做字符串拼接得到"[object Object]"。
{} + []開始的{}被解析為區(qū)塊語句而略過,相當(dāng)于+[],而[]轉(zhuǎn)為原始數(shù)據(jù)類型結(jié)果為"",強(qiáng)制轉(zhuǎn)換""為數(shù)字類型,結(jié)果為0;
[] + []將[]和{}分別調(diào)用ToPrimitive方法,返回""和"",然后做字符串拼接得到""。
{} + {}這個(gè)例子有爭(zhēng)議,對(duì)于Firefox和Edge瀏覽器來說,他們一以貫之的將第一個(gè){}作為區(qū)塊語句略過,而對(duì)于V8引擎系列(Chrome、Node.js等)則將第一個(gè){}解析為對(duì)象字面量。
這樣導(dǎo)致結(jié)果不一致,Firefox等解析語句為+{},對(duì)空對(duì)象{}強(qiáng)制轉(zhuǎn)為數(shù)字類型,即為+"[object Object]",將非空字符串轉(zhuǎn)換為數(shù)字類型,結(jié)果為NaN。
Chrome等解析語句為兩個(gè)空對(duì)象做加運(yùn)算,結(jié)果也比較好理解:"[object Object][object Object]"。
這是一個(gè)不嚴(yán)格等于運(yùn)算,我們來看轉(zhuǎn)換過程。
// 第一步,轉(zhuǎn)換右邊,根據(jù)上述原始數(shù)據(jù)類型轉(zhuǎn)換規(guī)則表, // 所有對(duì)象強(qiáng)制轉(zhuǎn) Boolean 類型都是 true,所以 ![] 為 false ToPrimitive(![]) >> ToPrimitive(!ToBoolean([])) >> ToPrimitive(!true) >> ToPrimitive(false) >> 0 // 第二步,轉(zhuǎn)換左邊 ToPrimitive([]) >> "" // 第三步,判斷 "" == 0 >> ToNumber("") == 0 >> 0 == 0 >> return true++[[]][+[]] + [+[]]
進(jìn)一步,來看這個(gè)例子。
很明顯,根據(jù)運(yùn)算符優(yōu)先級(jí),這個(gè)表達(dá)式可以用+分隔為左右兩個(gè)部分++[[]][+[]]和[+[]]。
可以看出這是一個(gè)數(shù)組里面有一個(gè)元素+[],而+[]即將[]強(qiáng)制轉(zhuǎn)換為數(shù)字類型,所以等于+"",結(jié)果為0。
綜上,右邊表達(dá)式轉(zhuǎn)換為[0]。
我們來一步步拆解, 根據(jù)對(duì)右邊表達(dá)式的轉(zhuǎn)換,這個(gè)表達(dá)式可以等同看做++([[]][0]),++后面又可以看做數(shù)組去第1個(gè)元素,表達(dá)式轉(zhuǎn)換為++[]。
但是當(dāng)我們?nèi)g覽器執(zhí)行++[]時(shí),報(bào)錯(cuò)了:Uncaught ReferenceError: Invalid left-hand side expression in prefix operation。
嚇得我趕緊去看++的語法,原來++的運(yùn)算是一種引用運(yùn)算,即++[]應(yīng)該轉(zhuǎn)換為:
var ref = [] ref = ref + 1
所以++[]轉(zhuǎn)換的正確姿勢(shì)為[] + 1。
左右進(jìn)行相加得到:[] + 1 + [0]。
根據(jù)ToPrimitive運(yùn)算規(guī)則,[] + 1 + [0] === "" + 1 + [0] === "1" + [0] === "10"。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/108893.html
摘要:所謂裝箱轉(zhuǎn)換,正是把基本類型轉(zhuǎn)換為對(duì)應(yīng)的對(duì)象,他是類型轉(zhuǎn)換中一種相當(dāng)重要的種類。拆箱轉(zhuǎn)換在標(biāo)準(zhǔn)中,規(guī)定了函數(shù),它是對(duì)象類型到基本類型的轉(zhuǎn)換即,拆箱轉(zhuǎn)換。拆箱轉(zhuǎn)換會(huì)嘗試調(diào)用和來獲得拆箱后的基本類型。 JavaScript隱式類型轉(zhuǎn)換 基本數(shù)據(jù)類型 ECMAScript 一共定義了七種 build-in types,其中六種為 Primitive Value,Null, Undefined...
摘要:在編程語言中,能夠表示并操作的值的類型稱做數(shù)據(jù)類型。中的原始類型包括數(shù)字,字符串和布爾值。日期與時(shí)間語言核心包括構(gòu)造函數(shù),用來創(chuàng)建表示日期和時(shí)間的對(duì)象。其規(guī)則為如果是布爾值,和分別被轉(zhuǎn)換為和如果是數(shù)字值,返回本身。 源代碼: https://github.com/RobinQu/Programing-In-Javascript/blob/master/chapters/Javas...
摘要:完整清單是中添加,此處不予介紹布爾值用來表示可能是真或假的值。結(jié)果抽象比較運(yùn)算符在比較它們之前在類型之間進(jìn)行自動(dòng)轉(zhuǎn)換。中的隱式轉(zhuǎn)換稱為強(qiáng)制類型轉(zhuǎn)換,并在規(guī)范中定義。這些內(nèi)置類型可用于在不同類型之間進(jìn)行顯式轉(zhuǎn)換。 翻譯:瘋狂的技術(shù)宅原文:https://www.valentinog.com/bl... 本文首發(fā)微信公眾號(hào):前端先鋒歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 show...
摘要:原始類型分別有類型類型和類型三種。類型中存在一個(gè)特殊的值叫。也可以把其他類型的數(shù)據(jù)自動(dòng)轉(zhuǎn)換為類型運(yùn)算符運(yùn)算符判斷原始類型語法結(jié)構(gòu)變量名稱。 數(shù)據(jù)類型 1.數(shù)據(jù)類型的概述;在JavaScript代碼中,能夠表示并且操作值的類型就叫做數(shù)據(jù)類型數(shù)據(jù)類型可以分成可變類型和不可變類型,可變類型的值是可以修改的。相反不可變類型的值是不可以修改的。數(shù)據(jù)類型還有原始類型(原始值)與引用類型(內(nèi)置對(duì)象)...
摘要:下面先看看涉及到的幾個(gè)函數(shù)以及他們的轉(zhuǎn)換規(guī)則,這個(gè)是需要記憶的內(nèi)容類型轉(zhuǎn)換需要使用到的函數(shù)對(duì)于布爾值用到的是對(duì)于數(shù)值,用到的是當(dāng)然還有但是對(duì)于隱式類型轉(zhuǎn)換的時(shí)候,調(diào)用的是前者。 javaScript類型轉(zhuǎn)換規(guī)則 javaScript的類型轉(zhuǎn)換其實(shí)一直是很多前端開發(fā)人員很迷的地方,一會(huì)兒這里要轉(zhuǎn)換,一會(huì)兒那里又要轉(zhuǎn)換,總之就是一個(gè)大寫的迷,因?yàn)樗[式類型轉(zhuǎn)換的地方實(shí)在是太多了。 但其實(shí)...
摘要:下面分幾步來簡單的探探不同類型的轉(zhuǎn)換吧以下的內(nèi)容,都可以從權(quán)威指南中找到。其他值轉(zhuǎn)換成在編寫代碼的過程中,幾乎不用考慮它的取值類型。核心內(nèi)置類,會(huì)嘗試先于可以理解為對(duì)象優(yōu)先轉(zhuǎn)換成數(shù)字例外的是,利用的是轉(zhuǎn)換。 最近在寫公司的登錄注冊(cè)模塊,遇到類型不同相比較的時(shí)候,就心驚膽戰(zhàn),每次都要用瀏覽器來驗(yàn)證一下,決定亂七八糟的隨便寫一下,方便日后自己回顧知識(shí)~ 弱類型帶來的那些讓人迷糊的事 弱類型...
閱讀 1809·2021-09-23 11:34
閱讀 2528·2021-09-22 15:45
閱讀 13337·2021-09-22 15:07
閱讀 2363·2021-09-02 15:40
閱讀 4255·2021-07-29 14:48
閱讀 1153·2019-08-30 15:55
閱讀 3308·2019-08-30 15:55
閱讀 2252·2019-08-30 15:55