摘要:如果返回值是一個(gè)原始值,則返回這個(gè)原始值。如果或者中的任意一個(gè)為字符串,則將另外一個(gè)也轉(zhuǎn)換成字符串,然后返回兩個(gè)字符串連接操作后的結(jié)果。因此,的結(jié)果實(shí)際上是兩個(gè)空字符串的連接。
原文:What is {} + {} in JavaScript?
譯者:justjavac
最近,Gary Bernhardt 在一個(gè)簡(jiǎn)短的演講視頻“Wat”中指出了一個(gè)有趣的 JavaScript 怪癖: 在把對(duì)象和數(shù)組混合相加時(shí),會(huì)得到一些意想不到的結(jié)果。 本篇文章會(huì)依次講解這些計(jì)算結(jié)果是如何得出的。
在 JavaScript 中,加法的規(guī)則其實(shí)很簡(jiǎn)單,只有兩種情況:
把數(shù)字和數(shù)字相加
把字符串和字符串相加
所有其他類(lèi)型的值都會(huì)被自動(dòng)轉(zhuǎn)換成這兩種類(lèi)型的值。 為了能夠弄明白這種隱式轉(zhuǎn)換是如何進(jìn)行的,我們首先需要搞懂一些基礎(chǔ)知識(shí)。
注意:在下面的文章中提到某一章節(jié)的時(shí)候(比如§9.1),指的都是 ECMA-262 語(yǔ)言規(guī)范(ECMAScript 5.1)中的章節(jié)。
讓我們快速的復(fù)習(xí)一下。 在 JavaScript 中,一共有兩種類(lèi)型的值:
原始值(primitives)
undefined
null
boolean
number
string
對(duì)象值(objects)。
除了原始值外,其他的所有值都是對(duì)象類(lèi)型的值,包括數(shù)組(array)和函數(shù)(function)。
類(lèi)型轉(zhuǎn)換加法運(yùn)算符會(huì)觸發(fā)三種類(lèi)型轉(zhuǎn)換:
轉(zhuǎn)換為原始值
轉(zhuǎn)換為數(shù)字
轉(zhuǎn)換為字符串
通過(guò) ToPrimitive() 將值轉(zhuǎn)換為原始值JavaScript 引擎內(nèi)部的抽象操作 ToPrimitive() 有著這樣的簽名:
ToPrimitive(input,PreferredType?)
可選參數(shù) PreferredType 可以是 Number 或者 String。 它只代表了一個(gè)轉(zhuǎn)換的偏好,轉(zhuǎn)換結(jié)果不一定必須是這個(gè)參數(shù)所指的類(lèi)型(汗),但轉(zhuǎn)換結(jié)果一定是一個(gè)原始值。 如果 PreferredType 被標(biāo)志為 Number,則會(huì)進(jìn)行下面的操作來(lái)轉(zhuǎn)換 input (§9.1):
如果 input 是個(gè)原始值,則直接返回它。
否則,如果 input 是一個(gè)對(duì)象。則調(diào)用 obj.valueOf() 方法。 如果返回值是一個(gè)原始值,則返回這個(gè)原始值。
否則,調(diào)用 obj.toString() 方法。 如果返回值是一個(gè)原始值,則返回這個(gè)原始值。
否則,拋出 TypeError 異常。
如果 PreferredType 被標(biāo)志為 String,則轉(zhuǎn)換操作的第二步和第三步的順序會(huì)調(diào)換。 如果沒(méi)有 PreferredType 這個(gè)參數(shù),則 PreferredType 的值會(huì)按照這樣的規(guī)則來(lái)自動(dòng)設(shè)置:
Date 類(lèi)型的對(duì)象會(huì)被設(shè)置為 String,
其它類(lèi)型的值會(huì)被設(shè)置為 Number。
通過(guò) ToNumber() 將值轉(zhuǎn)換為數(shù)字下面的表格解釋了 ToNumber() 是如何將原始值轉(zhuǎn)換成數(shù)字的 (§9.3)。
參數(shù) | 結(jié)果 |
---|---|
undefined | NaN |
null | +0 |
boolean | true被轉(zhuǎn)換為1,false轉(zhuǎn)換為+0 |
number | 無(wú)需轉(zhuǎn)換 |
string | 由字符串解析為數(shù)字。例如,"324"被轉(zhuǎn)換為324 |
如果輸入的值是一個(gè)對(duì)象,則會(huì)首先會(huì)調(diào)用 ToPrimitive(obj, Number) 將該對(duì)象轉(zhuǎn)換為原始值, 然后在調(diào)用 ToNumber() 將這個(gè)原始值轉(zhuǎn)換為數(shù)字。
通過(guò)ToString()將值轉(zhuǎn)換為字符串下面的表格解釋了 ToString() 是如何將原始值轉(zhuǎn)換成字符串的(§9.8)。
參數(shù) | 結(jié)果 |
---|---|
undefined | "undefined" |
null | "null" |
boolean | "true" 或者 "false" |
number | 數(shù)字作為字符串。比如,"1.765" |
string | 無(wú)需轉(zhuǎn)換 |
如果輸入的值是一個(gè)對(duì)象,則會(huì)首先會(huì)調(diào)用 ToPrimitive(obj, String) 將該對(duì)象轉(zhuǎn)換為原始值, 然后再調(diào)用 ToString() 將這個(gè)原始值轉(zhuǎn)換為字符串。
實(shí)踐一下下面的對(duì)象可以讓你看到引擎內(nèi)部的轉(zhuǎn)換過(guò)程。
var obj = { valueOf: function () { console.log("valueOf"); return {}; // not a primitive }, toString: function () { console.log("toString"); return {}; // not a primitive } }
Number 作為一個(gè)函數(shù)被調(diào)用(而不是作為構(gòu)造函數(shù)調(diào)用)時(shí),會(huì)在引擎內(nèi)部調(diào)用 ToNumber() 操作:
> Number(obj) valueOf toString TypeError: Cannot convert object to primitive value加法
有下面這樣的一個(gè)加法操作。
value1 + value2
在計(jì)算這個(gè)表達(dá)式時(shí),內(nèi)部的操作步驟是這樣的 (§11.6.1):
將兩個(gè)操作數(shù)轉(zhuǎn)換為原始值 (以下是數(shù)學(xué)表示法的偽代碼,不是可以運(yùn)行的 JavaScript 代碼):
prim1 := ToPrimitive(value1) prim2 := ToPrimitive(value2)
PreferredType 被省略,因此 Date 類(lèi)型的值采用 String,其他類(lèi)型的值采用 Number。
如果 prim1 或者 prim2 中的任意一個(gè)為字符串,則將另外一個(gè)也轉(zhuǎn)換成字符串,然后返回兩個(gè)字符串連接操作后的結(jié)果。
否則,將 prim1 和 prim2 都轉(zhuǎn)換為數(shù)字類(lèi)型,返回他們的和。
預(yù)料到的結(jié)果當(dāng)你將兩個(gè)數(shù)組相加時(shí),結(jié)果正是我們期望的:
> [] + [] ""
[] 被轉(zhuǎn)換成一個(gè)原始值:首先嘗試 valueOf() 方法,該方法返回?cái)?shù)組本身(this):
> var arr = []; > arr.valueOf() === arr true
此時(shí)結(jié)果不是原始值,所以再調(diào)用 toString() 方法,返回一個(gè)空字符串(string 是原始值)。 因此,[] + [] 的結(jié)果實(shí)際上是兩個(gè)空字符串的連接。
將一個(gè)數(shù)組和一個(gè)對(duì)象相加,結(jié)果依然符合我們的期望:
> [] + {} "[object Object]"
解析:將空對(duì)象轉(zhuǎn)換成字符串時(shí),產(chǎn)生如下結(jié)果。
> String({}) "[object Object]"
所以最終的結(jié)果其實(shí)是把 "" 和 "[object Object]" 兩個(gè)字符串連接起來(lái)。
更多的對(duì)象轉(zhuǎn)換為原始值的例子:
> 5 + new Number(7) 12 > 6 + { valueOf: function () { return 2 } } 8 > "abc" + { toString: function () { return "def" } } "abcdef"意想不到的結(jié)果
如果 + 加法運(yùn)算的第一個(gè)操作數(shù)是個(gè)空對(duì)象字面量,則會(huì)出現(xiàn)詭異的結(jié)果(Firefox console 中的運(yùn)行結(jié)果):
> {} + {} NaN
天哪!神馬情況?(譯注:原文沒(méi)有,是我第一次讀到這兒的時(shí)候感到太吃驚了,翻譯的時(shí)候加入的。) 這個(gè)問(wèn)題的原因是,JavaScript 把第一個(gè) {} 解釋成了一個(gè)空的代碼塊(code block)并忽略了它。 NaN 其實(shí)是表達(dá)式 +{} 計(jì)算的結(jié)果 (+ 加號(hào)以及第二個(gè) {})。 你在這里看到的 + 加號(hào)并不是二元運(yùn)算符「加法」,而是一個(gè)一元運(yùn)算符,作用是將它后面的操作數(shù)轉(zhuǎn)換成數(shù)字,和 Number() 函數(shù)完全一樣。例如:
> +"3.65" 3.65
以下的表達(dá)式是它的等價(jià)形式:
+{} Number({}) Number({}.toString()) // {}.valueOf() isn’t primitive Number("[object Object]") NaN
為什么第一個(gè) {} 會(huì)被解析成代碼塊(code block)呢? 因?yàn)檎麄€(gè)輸入被解析成了一個(gè)語(yǔ)句:如果左大括號(hào)出現(xiàn)在一條語(yǔ)句的開(kāi)頭,則這個(gè)左大括號(hào)會(huì)被解析成一個(gè)代碼塊的開(kāi)始。 所以,你也可以通過(guò)強(qiáng)制把輸入解析成一個(gè)表達(dá)式來(lái)修復(fù)這樣的計(jì)算結(jié)果: (譯注:我們期待它是個(gè)表達(dá)式,結(jié)果卻被解析成了語(yǔ)句,表達(dá)式和語(yǔ)句的區(qū)別可以查看我以前的『代碼之謎』系列的 語(yǔ)句與表達(dá)式。)
> ({} + {}) "[object Object][object Object]"
一個(gè)函數(shù)或方法的參數(shù)也會(huì)被解析成一個(gè)表達(dá)式:
> console.log({} + {}) [object Object][object Object]
經(jīng)過(guò)前面的講解,對(duì)于下面這樣的計(jì)算結(jié)果,你也應(yīng)該不會(huì)感到吃驚了:
> {} + [] 0
在解釋一次,上面的輸入被解析成了一個(gè)代碼塊后跟一個(gè)表達(dá)式 +[]。 轉(zhuǎn)換的步驟是這樣的:
+[] Number([]) Number([].toString()) // [].valueOf() isn’t primitive Number("") 0
有趣的是,Node.js 的 REPL 在解析類(lèi)似的輸入時(shí),與 Firefox 和 Chrome(和Node.js 一樣使用 V8 引擎) 的解析結(jié)果不同。 下面的輸入會(huì)被解析成一個(gè)表達(dá)式,結(jié)果更符合我們的預(yù)料:
> {} + {} "[object Object][object Object]" > {} + [] "[object Object]"3. 這就是所有嗎?
在大多數(shù)情況下,想要弄明白 JavaScript 中的 + 號(hào)是如何工作的并不難:你只能將數(shù)字和數(shù)字相加或者字符串和字符串相加。 對(duì)象值會(huì)被轉(zhuǎn)換成原始值后再進(jìn)行計(jì)算。如果將多個(gè)數(shù)組相加,可能會(huì)出現(xiàn)你意料之外的結(jié)果,相關(guān)文章請(qǐng)參考在 javascript 中,為什么 [1,2] + [3,4] 不等于 [1,2,3,4]? 和 為什么 ++[[]][+[]]+[+[]] = 10?。
如果你想連接多個(gè)數(shù)組,需要使用數(shù)組的 concat 方法:
> [1, 2].concat([3, 4]) [1, 2, 3, 4]
JavaScript 中沒(méi)有內(nèi)置的方法來(lái)“連接" (合并)多個(gè)對(duì)象。 你可以使用一個(gè) JavaScript 庫(kù),比如 Underscore:
> var o1 = {eeny:1, meeny:2}; > var o2 = {miny:3, moe: 4}; > _.extend(o1, o2) {eeny: 1, meeny: 2, miny: 3, moe: 4}
注意:和 Array.prototype.concat() 方法不同,extend() 方法會(huì)修改它的第一個(gè)參數(shù),而不是返回合并后的對(duì)象:
> o1 {eeny: 1, meeny: 2, miny: 3, moe: 4} > o2 {miny: 3, moe: 4}
如果你想了解更多有趣的關(guān)于運(yùn)算符的知識(shí),你可以閱讀一下 “Fake operator overloading in JavaScript”(中文正在翻譯中)。
參考JavaScript 并非所有的東西都是對(duì)象
JavaScript:將所有值都轉(zhuǎn)換成對(duì)象
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/77927.html
摘要:中有很多奇妙的東西,歸咎歸功于設(shè)計(jì)時(shí)候的迅速。缺陷有,但是的強(qiáng)大確實(shí)體現(xiàn)的淋漓盡致。它是如此的靈活,當(dāng)然隨之而來(lái)的便是開(kāi)發(fā)的代價(jià),它不像強(qiáng)類(lèi)型語(yǔ)言那樣規(guī)規(guī)矩矩。難得周末晚上清閑,回味這些看起來(lái)有點(diǎn)怪怪卻又在發(fā)生著的問(wèn)題。 JavaScript中有很多奇妙的東西,歸咎or歸功于設(shè)計(jì)時(shí)候的迅速。缺陷有,但是JavaScript的強(qiáng)大確實(shí)體現(xiàn)的淋漓盡致。 它是如此的靈活,當(dāng)然隨之而來(lái)的便是開(kāi)...
摘要:報(bào)錯(cuò)報(bào)錯(cuò)報(bào)錯(cuò)有前導(dǎo)的數(shù)值會(huì)被視為八進(jìn)制,但是如果前導(dǎo)后面有數(shù)字和,則該數(shù)值被視為十進(jìn)制。對(duì)于那些會(huì)自動(dòng)轉(zhuǎn)為科學(xué)計(jì)數(shù)法的數(shù)字,會(huì)將科學(xué)計(jì)數(shù)法的表示方法視為字符串,因此導(dǎo)致一些奇怪的結(jié)果。 1.概述1.1整數(shù)和浮點(diǎn)數(shù)1.2數(shù)值精度1.3數(shù)值范圍2.數(shù)值的表示法3.數(shù)值的進(jìn)制4.特殊數(shù)值4.1正零和負(fù)零4.2NaN4.3Infinity5.與數(shù)值相關(guān)的全局方法5.1parseInt()5.2...
摘要:之所以把歸并排序快速排序希爾排序堆排序放在一起比較,是因?yàn)樗鼈兊钠骄鶗r(shí)間復(fù)雜度都為。歸并排序是一種穩(wěn)定的排序方法。因此,快速排序并不穩(wěn)定。希爾排序思想先將整個(gè)待排序的記錄序列分割成為若干子序列。 showImg(https://segmentfault.com/img/bVbvpYZ?w=900&h=250); 1. 前言 算法為王。 想學(xué)好前端,先練好內(nèi)功,只有內(nèi)功深厚者,前端之路才...
摘要:先說(shuō)下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問(wèn)到相關(guān)問(wèn)題,其他公司壓根沒(méi)問(wèn)。我自己回答的是自己開(kāi)發(fā)組件面臨的問(wèn)題。完全不用擔(dān)心對(duì)方到時(shí)候打電話(huà)核對(duì)的問(wèn)題。 2019的5月9號(hào),離發(fā)工資還有1天的時(shí)候,我的領(lǐng)導(dǎo)親切把我叫到辦公室跟我說(shuō):阿郭,我們公司要倒閉了,錢(qián)是沒(méi)有的啦,為了不耽誤你,你趕緊出去找工作吧。聽(tīng)到這話(huà),我虎軀一震,這已經(jīng)是第2個(gè)月沒(méi)工資了。 公...
摘要:原文鏈接歡迎現(xiàn)在有塊錢(qián)人民幣,將塊錢(qián)換成零錢(qián)最小幣值元,一共有多少方式總的不同方式的數(shù)目等于將現(xiàn)金數(shù)換成除第一種幣值之外的所有其他硬幣的不同方式數(shù)據(jù),加上將現(xiàn)金數(shù)第一種幣值換成所有種類(lèi)的幣值的不同方式,根據(jù)上面的說(shuō)法來(lái)實(shí)現(xiàn)吧實(shí)現(xiàn)中的是中的 原文鏈接: 歡迎 Star 現(xiàn)在有100塊錢(qián)人民幣,將 100 塊錢(qián)換成零錢(qián)(最小幣值 1 元),一共有多少方式? 總的不同方式的數(shù)目等于: 將現(xiàn)...
閱讀 1160·2021-11-24 09:38
閱讀 3609·2021-11-22 15:32
閱讀 3461·2019-08-30 15:54
閱讀 2574·2019-08-30 15:53
閱讀 1499·2019-08-30 15:52
閱讀 2535·2019-08-30 13:15
閱讀 1841·2019-08-29 12:21
閱讀 1402·2019-08-26 18:36