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