摘要:對于與的設計在中所設計的純對象類型的與方法,它們的返回如下方法返回值對象本身。與三個強制轉換函數,所對應的就是在標準中的三個內部運算轉換的對照表。
在JS中的運算符共同的情況中,(+)符號是很常見的一種,它有以下的使用情況:
數字的加法運算,二元運算
字符串的連接運算,二元運算,最高優先
正號,一元運算,可延伸為強制轉換其他類型的運算元為數字類型
當然,如果考慮多個符號一起使用時,(+=)與(++)又是另外的用途。
另一個常見的是花括號({}),它有兩個用途也很常見:
對象的字面文字定義
區塊語句
所以,要能回答這個問題,要先搞清楚重點是什么?
第一個重點是:
加號(+)運算在JS中在使用上的規定是什么。
第二個重點則是:
加號運算符(+)對象在JS中是怎么轉換為原始數據類型的值的。
除了上面說明的常見情況外,在標準中轉換的規則還有以下幾個,要注意它的順序:
operand + operand = result
使用ToPrimitive運算轉換左與右運算元為原始數據類型值(primitive)
在第1步轉換后,如果有運算元出現原始數據類型是"字符串"類型值時,則另一運算元作強制轉換為字符串,然后作字符串的連接運算(concatenation)
在其他情況時,所有運算元都會轉換為原始數據類型的"數字"類型值,然后作數學的相加運算(addition)
ToPrimitive內部運算因此,加號運算符只能使用于原始數據類型,那么對于對象類型的值,要如何轉換為原始數據類型?下面說明是如何轉換為原始數據類型的。
在ECMAScript 6th Edition #7.1.1,有一個抽象的ToPrimitive運算,它會用于對象轉換為原始數據類型,這個運算不只會用在加號運算符,也會用在關系比較或值相等比較的運算中。下面有關于ToPrimitive的說明語法:
ToPrimitive(input, PreferredType?)
input代表代入的值,而PreferredType可以是數字(Number)或字符串(String)其中一種,這會代表"優先的"、"首選的"的要進行轉換到哪一種原始類型,轉換的步驟會依這里的值而有所不同。但如果沒有提供這個值也就是預設情況,則會設置轉換的hint值為"default"。這個首選的轉換原始類型的指示(hint值),是在作內部轉換時由JS視情況自動加上的,一般情況就是預設值。
而在JS的Object原型的設計中,都一定會有兩個valueOf與toString方法,所以這兩個方法在所有對象里面都會有,不過它們在轉換有可能會交換被調用的順序。
當PreferredType為數字(Number)時當PreferredType為數字(Number)時,input為要被轉換的值,以下是轉換這個input值的步驟:
如果input是原始數據類型,則直接返回input。
否則,如果input是個對象時,則調用對象的valueOf()方法,如果能得到原始數據類型的值,則返回這個值。
否則,如果input是個對象時,調用對象的toString()方法,如果能得到原始數據類型的值,則返回這個值。
否則,拋出TypeError錯誤。
當PreferredType為字符串(String)時上面的步驟2與3對調,如同下面所說:
如果input是原始數據類型,則直接返回input。
否則,如果input是個對象時,調用對象的toString()方法,如果能得到原始數據類型的值,則返回這個值。
否則,如果input是個對象時,則調用對象的valueOf()方法,如果能得到原始數據類型的值,則返回這個值。
否則,拋出TypeError錯誤。
PreferredType沒提供時,也就是hint為"default"時與PreferredType為數字(Number)時的步驟相同。
數字其實是預設的首選類型,也就是說在一般情況下,加號運算中的對象要作轉型時,都是先調用valueOf再調用toString。
但這有兩個異常,一個是Date對象,另一是Symbol對象,它們覆蓋了原來的PreferredType行為,Date對象的預設首選類型是字符串(String)。
模擬代碼說明因此你會看到在一些教程文件上會區分為兩大類對象,一類是 Date 對象,另一類叫 非Date(non-date) 對象。因為這兩大類的對象在進行轉換為原始數據類型時,首選類型恰好相反。
以簡單的模擬代碼來說明,加號運算符(+)的運行過程就是像下面這個模擬碼一樣,我想這會很容易理解:
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))
步驟簡單來說就是,運算元都用ToPrimitive先轉換為原始數據類型,然后其一是字符串時,使用ToString強制轉換另一個運算元,然后作字符串連接運算。要不然,就是都使用ToNumber強制轉換為數字作加法運算。
而ToPrimitive在遇到對象類型時,預設調用方式是先調用valueOf再調用toString,一般情況數字類型是首選類型。
上面說的ToString與ToNumber這兩個也是JS內部的抽象運算。
valueOf與toString方法valueOf與ToString是在Object中的兩個必有的方法,位于Object.prototype上,它是對象要轉為原始數據類型的兩個姐妹方法。從上面的內容已經可以看到,ToPrimitive這個抽象的內部運算,會依照設置的首選的類型,決定要先后調用valueOf與toString方法的順序,當數字為首選類型時,優先使用valueOf,然后再調用toString。當字符串為首選類型時,則是相反的順序。預設調用方式則是如數字首選類型一樣,是先調用valueOf再調用toString。
JS對于Object與Array的設計在JS中所設計的Object純對象類型的valueOf與toString方法,它們的返回如下:
valueOf方法返回值: 對象本身。
toString方法返回值: "[object Object]"字符串值,不同的內建對象的返回值是"[object type]"字符串,"type"指的是對象本身的類型識別,例如Math對象是返回"[object Math]"字符串。但有些內建對象因為覆蓋了這個方法,所以直接調用時不是這種值。(注意: 這個返回字符串的前面的"object"開頭英文是小寫,后面開頭英文是大寫)
你有可能會看過,利用Object中的toString來進行各種不同對象的判斷語法,這在以前JS能用的函數庫或方法不多的年代經常看到,不過它需要配合使用函數中的call方法,才能輸出正確的對象類型值,例如:
> Object.prototype.toString.call([]) "[object Array]" > Object.prototype.toString.call(new Date) "[object Date]"
所以,從上面的內容就可以知道,下面的這段代碼的結果會是調用到toString方法(因為valueOf方法的返回并不是原始的數據類型):
> 1 + {} "1[object Object]"
一元正號(+),具有讓首選類型(也就是hint)設置為數字(Number)的功能,所以可以強制讓對象轉為數字類型,一般的對象會轉為:
> +{} //相當于 +"[object Object]" NaN
當然,對象的這兩個方法都可以被覆蓋,你可以用下面的代碼來觀察這兩個方法的運行順序,下面這個都是先調用valueOf的情況:
let obj = { valueOf: function () { console.log("valueOf"); return {}; // object }, toString: function () { console.log("toString"); return "obj"; // string } } console.log(1 + obj); //valueOf -> toString -> "1obj" console.log(+obj); //valueOf -> toString -> NaN console.log("" + obj); //valueOf -> toString -> "obj"
先調用toString的情況比較少見,大概只有Date對象或強制要轉換為字符串時才會看到:
let obj = { valueOf: function () { console.log("valueOf"); return 1; // number }, toString: function () { console.log("toString"); return {}; // object } } alert(obj); //toString -> valueOf -> alert("1"); String(obj); //toString -> valueOf -> "1";
而下面這個例子會造成錯誤,因為不論順序是如何都得不到原始數據類型的值,錯誤消息是"TypeError: Cannot convert object to primitive value",從這個消息中很明白的告訴你,它這里面會需要轉換對象到原始數據類型:
let obj = { valueOf: function () { console.log("valueOf"); return {}; // object }, toString: function () { console.log("toString"); return {}; // object } } console.log(obj + obj); //valueOf -> toString -> error!
Array(數組)很常用到,雖然它是個對象類型,但它與Object的設計不同,它的toString有覆蓋,說明一下數組的valueOf與toString的兩個方法的返回值:
valueOf方法返回值: 對象本身。(與Object一樣)
toString方法返回值: 相當于用數組值調用join(",")所返回的字符串。也就是[1,2,3].toString()會是"1,2,3",這點要特別注意。
Function對象很少會用到,它的toString也有被覆蓋,所以并不是Object中的那個toString,Function對象的valueOf與toString的兩個方法的返回值:
valueOf方法返回值: 對象本身。(與Object一樣)
toString方法返回值: 函數中包含的代碼轉為字符串值
Number、String、Boolean三個包裝對象包裝對象是JS為原始數據類型數字、字符串、布爾專門設計的對象,所有的這三種原始數據類型所使用到的屬性與方法,都是在這上面所提供。
包裝對象的valueOf與toString的兩個方法在原型上有經過覆蓋,所以它們的返回值與一般的Object的設計不同:
valueOf方法返回值: 對應的原始數據類型值
toString方法返回值: 對應的原始數據類型值,轉換為字符串類型時的字符串值
toString方法會比較特別,這三個包裝對象里的toString的細部說明如下:
Number包裝對象的toString方法: 可以有一個傳參,可以決定轉換為字符串時的進位(2、8、16)
String包裝對象的toString方法: 與String包裝對象中的valueOf相同返回結果
Boolean包裝對象的toString方法: 返回"true"或"false"字符串
另外,常被搞混的是直接使用Number()、String()與Boolean()三個強制轉換函數的用法,這與包裝對象的用法不同,包裝對象是必須使用new關鍵字進行對象實例化的,例如new Number(123),而Number("123")則是強制轉換其他類型為數字類型的函數。
Number()、String()與Boolean()三個強制轉換函數,所對應的就是在ECMAScript標準中的ToNumber、ToString、ToBoolean三個內部運算轉換的對照表。而當它們要轉換對象類型前,會先用上面說的ToPrimitive先轉換對象為原始數據類型,再進行轉換到所要的類型值。
不管如何,包裝對象很少會被使用到,一般我們只會直接使用原始數據類型的值。而強制轉換函數因為也有替換的語法,它們會被用到的機會也不多。
實例 字符串 + 其他原始類型字符串在加號運算有最高的優先運算,與字符串相加必定是字符串連接運算(concatenation)。所有的其他原始數據類型轉為字符串,可以參考ECMAScript標準中的ToString對照表,以下為一些簡單的例子:
> "1" + 123 "1123" > "1" + false "1false" > "1" + null "1null" > "1" + undefined "1undefined"數字 + 其他的非字符串的原始數據類型
數字與其他類型作相加時,除了字符串會優先使用字符串連接運算(concatenation)的,其他都要依照數字為優先,所以除了字符串之外的其他原始數據類型,都要轉換為數字來進行數學的相加運算。如果明白這項規則,就會很容易的得出加法運算的結果。
所有轉為數字類型可以參考ECMAScript標準中的ToNumber對照表,以下為一些簡單的例子:
> 1 + true //true轉為1, false轉為0 2 > 1 + null //null轉為0 1 > 1 + undefined //undefined轉為NaN NaN數字/字符串以外的原始數據類型作加法運算
當數字與字符串以外的,其他原始數據類型直接使用加號運算時,就是轉為數字再運算,這與字符串完全無關。
> true + true 2 > true + null 1 > undefined + null NaN空數組 + 空數組
> [] + [] ""
兩個數組相加,依然按照valueOf -> toString的順序,但因為valueOf是數組本身,所以會以toString的返回值才是原始數據類型,也就是空字符串,所以這個運算相當于兩個空字符串在相加,依照加法運算規則第2步驟,是字符串連接運算(concatenation),兩個空字符串連接最后得出一個空字符串。
空對象 + 空對象> {} + {} "[object Object][object Object]"
兩個空對象相加,依然按照valueOf -> toString的順序,但因為valueOf是對象本身,所以會以toString的返回值才是原始數據類型,也就是"[object Object]"字符串,所以這個運算相當于兩個"[object Object]"字符串在相加,依照加法運算規則第2步驟,是字符串連接運算(concatenation),最后得出一個"object Object"字符串。
但是這個結果有異常,上面的結果只是在Chrome瀏覽器上的結果(v55),怎么說呢?
有些瀏覽器例如Firefox、Edge瀏覽器會把{} + {}直譯為相當于+{}語句,因為它們會認為以花括號開頭({)的,是一個區塊語句的開頭,而不是一個對象字面量,所以會認為略過第一個{},把整個語句認為是個+{}的語句,也就是相當于強制求出數字值的Number({})函數調用運算,相當于Number("[object Object]")運算,最后得出的是NaN。
特別注意: {} + {}在不同的瀏覽器有不同結果
如果在第一個(前面)的空對象加上圓括號(()),這樣JS就會認為前面是個對象,就可以得出同樣的結果:
> ({}) + {} "[object Object][object Object]"
或是分開來先聲明對象的變量值,也可以得出同樣的結果,像下面這樣:
> let foo = {}, bar = {}; > foo + bar;
空對象 + 空數組注: 上面說的行為這與加號運算的第一個(前面)的對象字面值是不是個空對象無關,就算是里面有值的對象字面,例如{a:1, b:2},也是同樣的結果。
注: 上面說的Chrome瀏覽器是在v55版本中的主控臺直接運行的結果。其它舊版本有可能并非此結果。
上面同樣的把{}當作區塊語句的情況又會發生,不過這次所有的瀏覽器都會有一致結果,如果{}(空對象)在前面,而[](空數組)在后面時,前面(左邊)那個運算元會被認為是區塊語句而不是對象字面量。
所以{} + []相當于+[]語句,也就是相當于強制求出數字值的Number([])運算,相當于Number("")運算,最后得出的是0數字。
> {} + [] 0 > [] + {} "[object Object]"
Date對象特別注意: 所以如果第一個(前面)是{}時,后面加上其他的像數組、數字或字符串,這時候加號運算會直接變為一元正號運算,也就是強制轉為數字的運算。這是個陷阱要小心。
Date對象的valueOf與toString的兩個方法的返回值:
valueOf方法返回值: 給定的時間轉為UNIX時間(自1 January 1970 00:00:00 UTC起算),但是以微秒計算的數字值
toString方法返回值: 本地化的時間字符串
Date對象上面有提及是首選類型為"字符串"的一種異常的對象,這與其他的對象的行為不同(一般對象會先調用valueOf再調用toString),在進行加號運算時時,它會優先使用toString來進行轉換,最后必定是字符串連接運算(concatenation),例如以下的結果:
> 1 + (new Date()) > "1Sun Nov 27 2016 01:09:03 GMT+0800 (CST)"
要得出Date對象中的valueOf返回值,需要使用一元加號(+),來強制轉換它為數字類型,例如以下的代碼:
> +new Date() 1480180751492Symbols類型
ES6中新加入的Symbols數據類型,它不算是一般的值也不是對象,它并沒有內部自動轉型的設計,所以完全不能直接用于加法運算,使用時會報錯。
總結{} + {}的結果是會因瀏覽器而有不同結果,Chrome(v55)中是[object Object][object Object]字符串連接,但其它的瀏覽器則是認為相當于+{}運算,得出NaN數字類型。
{} + []的結果是相當于+[],結果是0數字類型。
參考文章What is {} + {} in JavaScript?
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88094.html
摘要:與此相對,強類型語言的類型之間不一定有隱式轉換。三為什么是弱類型弱類型相對于強類型來說類型檢查更不嚴格,比如說允許變量類型的隱式轉換,允許強制類型轉換等等。在中,加性運算符有大量的特殊行為。 從++[[]][+[]]+[+[]]==10?深入淺出弱類型JS的隱式轉換 本文純屬原創? 如有雷同? 純屬抄襲? 不甚榮幸! 歡迎轉載! 原文收錄在【我的GitHub博客】,覺得本文寫的不算爛的...
摘要:事后策略指感知到有爬蟲事件后,采取的封禁驗證碼等組合策略。三反爬架構什么樣數據支撐平臺通過埋點采集等方式接入各類維度的基礎數據和第三方指紋,封裝成統一的數據輸出形式。 一、反爬體系要做什么 完整的反爬體系有三大部分工作要做:感知識別、策略分析、監控封禁。 (一)感知識別: 數據支撐:爬蟲指紋、設備指紋、風險UA、IP庫等,不同端指紋的mapping等。 數據感知,什么人,通過什么方式...
摘要:說明在比較的時候,會進行隱式轉換,你如果對隱式轉換不是特別熟悉,結果往往出乎你的意料。解釋相信我,這行代碼是簡單的,它并不復雜,我們先來分解一下這行代碼我們把這一行,分解成了行了。簡單說中的與方法簡單說與引發的思考 說明 JavaScript在比較的時候,會進行隱式轉換,你如果對隱式轉換不是特別熟悉,結果往往出乎你的意料。 我們來看看這行代碼 (![]+[])[+!![]- -+!!...
摘要:說明在比較的時候,會進行隱式轉換,你如果對隱式轉換不是特別熟悉,結果往往出乎你的意料。解釋相信我,這行代碼是簡單的,它并不復雜,我們先來分解一下這行代碼我們把這一行,分解成了行了。簡單說中的與方法簡單說與引發的思考 說明 JavaScript在比較的時候,會進行隱式轉換,你如果對隱式轉換不是特別熟悉,結果往往出乎你的意料。 我們來看看這行代碼 (![]+[])[+!![]- -+!!...
閱讀 881·2023-04-25 19:17
閱讀 2179·2021-09-10 11:26
閱讀 1898·2019-08-30 15:54
閱讀 3411·2019-08-30 15:53
閱讀 2681·2019-08-30 11:20
閱讀 3391·2019-08-29 15:12
閱讀 1229·2019-08-29 13:16
閱讀 2384·2019-08-26 12:19