你盼世界,我盼望你無bug
。Hello 你們好!我是霖呆呆!javascript
那年我十八歲,單純,善良,懵懂,青澀,陽光,可愛...前端
如今的我...在面對JS
類型轉換的時候,依舊是...vue
我覺得了解了toString()
和valueOf()
以後,我就是那個最懂你的男人...java
直到我在你的內心看到一個叫作Symbol.toPrimitive
的人...面試
這我的,他掌握着你轉換的核心,甚至在必要的時候可以徹底替代toString()
和valueOf()
。segmentfault
我奔潰了...發瘋似得去找谷哥和度娘,求他們告訴我打敗Symbol.toPrimitive
的法門,最後,他們只說了一句話:數組
"看完霖呆呆的這篇文章再來一波三連就能夠了啊!!!"
瀏覽器
😂😂😂函數
抱歉,狗改不了吃屎,呸,秉性難移頑梗不化,忍不住寫了個小短片哈 😄。post
其實也是想要告訴你們,上一篇文章是重點,這篇文章也是重點,因此都要好好看哦。
有不少人以爲花了這麼多的時間和這麼多的精力來看呆呆的這兩篇類型轉換,就只爲了弄懂這一個小小的知識點,感受好虧啊,遠沒有刷一篇各個知識點都覆蓋的面試總結的成就感高。其實我想說,面試總結類的文章當然能夠幫助咱們查漏補缺,可是對於JS
基礎知識的掌握我認爲也是十分重要的。就像今天看到Minute老哥的一篇《非科班二本前端大廠面試的心路歷程和總結(騰訊、頭條、阿里、京東) | 掘金技術徵文》文章裏說的同樣:
OK👌,玩歸玩,鬧歸鬧,類型轉換把你教。
來看看經過閱讀你能夠學習到:
toString
和valueOf
Symbol.toPrimitive
==
比較時的類型轉換+、-、*、/、%
的類型轉換在正式閱讀以前,我推薦你看一下本系列的上一篇《從206個console.log()徹底弄懂數據類型轉換的前世此生(上)》;這樣有利於你閱讀本篇文章。
讓咱們來回顧一下以前提到的toPrimitive
執行流程:
看完了上篇的對象轉字符串,不知道你對toPrimititve
的轉換流程掌握了多少呢?
若是你感受以前的那些例子還不太具備說明性,也就是說你仍是沒有感受到JS
確實是按我畫的那個流程圖來進行轉換的話,你能夠看看這裏。
咱們在上篇的6.1
中提到過了,大部分的對象都是能夠經過原型鏈查找到Object.prototype
上的toString
或者valueOf
方法,而後使用它們。
可是你想一想,若是我這個對象自己就有toString
或者valueOf
方法的話是否是就能夠不用Object.prototype
上的了,這其實就是咱們常聽到的重寫。
你也許能夠用這樣的方式來覆蓋原型鏈上的這兩個方法:
let obj = {
toString () {
return '1'
},
valueOf () {
return 1
}
}
複製代碼
甚至你還能夠直接修改Object.prototype
上的方法:
Object.prototype.toString = function () {
return 1
}
var b = {}
console.log(String(b)) // '1'
複製代碼
(固然,這種確定是不推薦的哈,這會影響全部的對象)
(經過重寫toString()
和valueOf()
來判斷咱們以前的toPrimitive
流程是否正確)
上面👆兩個例子我只是想告訴你,既然咱們能夠重寫對象上的toString()
和valueOf
,那若是咱們在重寫的函數裏面再加上console.log(xxx)
,不就能夠知道對象轉原始值的具體過程是否是按咱們設想的方式執行下去了嗎?
好比這樣:
var b = {
toString () {
console.log('toString')
return 1
},
valueOf () {
console.log('valueOf')
return [1, 2]
},
}
console.log(Number(b))
複製代碼
想一下這裏的執行結果 🤔️?
既然是用Number()
方法來進行轉換的話,那也就是執行了僞代碼toPrimitive(obj, 'number')
了。
那也就是說會先調用valueOf()
函數,而後判斷這個函數的返回值是否爲原始值,再決定是繼續調用toString()
仍是返回。
valueOf()
被重寫了,因此調用valueOf()
以後返回的是一個引用類型[1, 2]
,因此它會繼續執行toString()
[1, 2].toString()
,可是這時候的toString()
也是被重寫了的而且返回了數字1
,因此咱們根本不必管[1, 2].toString()
的結果了,而是直接將1
返回。因此整個過程結束以後,答案爲:
'valueof'
'toString'
1
複製代碼
這樣看下來流程就很清晰了,它確實是按照咱們預期的方向走的。
若是你理解了上面一題的話,咱再來看看這裏:
var b = {
toString () {
console.log('toString')
return { name: 'b' }
},
valueOf () {
console.log('valueOf')
return [1, 2]
},
}
console.log(String(b))
複製代碼
此次我是用的String()
方法將b
轉爲字符串。
並且要注意了,重寫的toString()
和valueOf
都是返回的引用數據類型。那你能夠想一想到最後的結果會是什麼嗎?
來看看過程分析:
toString()
方法,返回引用類型{name: 'b'}
valueOf()
方法,返回引用類型[1,2]
沒錯,這裏的轉換過程最終是失敗了的,由於還記得流程圖中,最後一步了若還不是原始值的話,就會拋異常了。
因此結果爲:
'toString'
'valueOf'
Cannot convert object to primitive value at String
複製代碼
精彩精彩👏,我彷彿已經看到了徹底弄懂對象轉換原始值機制的曙光!!!
(數組在進行ToString
時的不一樣之處)
咱們都知道,當數組在進行轉字符串的時候,會把裏面的每一項都轉爲字符串而後再進行","
拼接返回。
那麼爲何會有","
拼接這一步呢?難道toString()
在調用的時候還會調用join()
方法嗎?
爲了驗證個人想法💡,我作了一個實驗,重寫了一個數組的join()
方法:
var arr = [1, 2]
arr['join'] = function () {
let target = [...this]
return target.join('~')
}
console.log(String(arr))
複製代碼
重寫的join
函數中,this
表示的就是調用的這個數組arr
。
而後將返回值改成"~"
拼接,結果答案居然是:
"1~2"
複製代碼
也就是說在String(arr)
的過程當中,它確實是隱式調用了join
方法。
可是當咱們重寫了toString()
以後,就不會管這個重寫的join
了:
var arr = [1, 2]
arr['toString'] = function () {
let target = [...this]
return target.join('*')
}
arr['join'] = function () {
let target = [...this]
return target.join('~')
}
console.log(String(arr)) // "1*2"
複製代碼
能夠看出toString()
的優先級仍是比join()
高的。
如今咱們又能夠得出一個結論:
對象若是是數組的話,當咱們不重寫其toString()
方法,在轉換爲字符串類型的時候,默認實現就是將調用join()
方法的返回值做爲toString()
的返回值。
在我正爲本身弄懂了toPrimitive
而感到驕傲的時候,我得知了一個叫作Symbol.toPrimitive
的傢伙。
看這傢伙的樣子,讓我想起了之前見到過的一些老大哥:Symbol.hasInstance
、Symbol.toStringTag
。
他們都有着酷酷的紋身:Symbol
,而且以前的老大哥是可以讓咱們作一些自定義的事情,不知道這傢伙是否是和我想的同樣,也可以幫助咱們重寫toPrimitive
🤔️?
瞭解了事情的真相以後,我知道了本身仍是不笨的,給猜對了。
Symbol.toPrimitive
就是比重寫toString()
和valueOf()
更屌的一個屬性。
若是你在一個對象裏重寫了它的話,那麼甚至都不會執行重寫的toString()
和valueOf()
了。
(Symbol.toPrimitive
也被叫作@@toPrimitive
)
(Symbol.toPrimitive
的基本使用-返回值爲一個原始值)
你不信霖呆呆說的話?咱給整一個?
var b = {
toString () {
console.log('toString')
return { name: 'b' }
},
valueOf () {
console.log('valueOf')
return [1, 2]
},
[Symbol.toPrimitive] () {
console.log('symbol')
return '1'
}
}
console.log(String(b))
console.log(Number(b))
複製代碼
這道題中,我把剛剛提到的三個屬性都給重寫了,你感受結果會是什麼?
😄記住呆呆剛剛說的話,Symbol.toPrimitive
的優先級是最高的,因此這裏只會執行它裏面的內容。
所以結果爲:
'symbol'
'1'
'symbol'
1
複製代碼
而且你們能夠看到,雖然Symbol.toPrimitive
的返回值是"1"
,可是最終的結果String(b)
仍是字符串,Number(b)
仍是數字,代表,最後仍是會給返回值作一層對應的轉換的。
(Symbol.toPrimitive
的返回值爲引用類型,或者沒有返回值?)
若是它的返回值是引用類型,或者乾脆沒有返回值,就會繼續執行valueOf
或者toString
嗎?
結果並不會...來看看這裏,我定義了對象b和c
,而且重寫了這三個屬性:
var b = {
toString () {
console.log('b.toString')
return { name: 'b' }
},
valueOf () {
console.log('b.valueOf')
return [1, 2]
},
[Symbol.toPrimitive] () {
console.log('b.symbol')
}
}
var c = {
toString () {
console.log('c.toString')
return { name: 'c' }
},
valueOf () {
console.log('c.valueOf')
return [1, 2]
},
[Symbol.toPrimitive] () {
console.log('c.symbol')
return [1, 2]
}
}
console.log(String(b))
console.log(String(c))
複製代碼
執行結果:
'b.symbol'
'undefined'
'c.symbol'
TypeError: Cannot convert object to primitive value
at String
複製代碼
過程分析:
String(b)
過程當中打印出了b.symbol
,說明仍是執行了Symbol.toPrimitive
方法的,可是這個方法並無返回值,且也沒有繼續執行valueOf()
或者toString()
了,而是返回了字符串"undefined"
String(c)
過程當中也打印了c.symbol
,可是Symbol.toPrimitive
的返回值是一個對象,卻報錯了。因此從這道題,咱們能夠看出:
Symbol.toPrimitive
它可謂是一夫當關,萬夫莫開
,只要有它在,就不會繼續往下走了,它的返回結果就是做爲最終的返回結果。
並且經過String(c)
咱們能夠看出來:若是返回的是一個對象的話,也不會繼續執行valueOf()、toString()
了,而是判斷它的返回值,若是是原始值那就返回,不然就拋出錯誤。
(帶參數的Symbol.toPrimitive
)
你覺得Symbol.toPrimitive
僅僅是這麼簡單嗎?
No😺,它居然還能接收參數!!!
它接收一個字符串類型的參數:hint
,表示要轉換到的原始值的預期類型。
且參數的取值爲如下字符串的其中一個:
"number"
"string"
"default"
嗯😺?霖呆呆我一驚,這怎麼和以前介紹的toPrimitive
那麼像啊:
toPrimitive(obj, 'number')
toPrimitive(obj, 'string')
複製代碼
也就是說傳入了以後,就是告訴Symbol.toPrimitive
要轉換成哪一個類型咯?
這麼屌的功能,趕忙來試試:
var b = {
toString () {
console.log('toString')
return '1'
},
valueOf () {
console.log('valueOf')
return [1, 2]
},
[Symbol.toPrimitive] (hint) {
console.log('symbol')
if (hint === 'string') {
console.log('string')
return '1'
}
if (hint === 'number') {
console.log('number')
return 1
}
if (hint === 'default') {
console.log('default')
return 'default'
}
}
}
console.log(String(b))
console.log(Number(b))
複製代碼
這道題重寫了toString、valueOf、Symbol.toPrimitive
三個屬性,經過上面👆的題目咱們已經知道了只要有Symbol.toPrimitive
在,前面兩個屬性就被忽略了,因此咱們不用管它們。
而對於Symbol.toPrimitive
,我將三種hint
的狀況都寫上了,若是按照個人設想的話,在調用String(b)
的時候應該是要打印出string
的,調用Number(b)
打印出number
,結果也正如我所預想的同樣:
'string'
'1'
'number'
1
複製代碼
那麼這裏面的"default"
是作什麼的呀?它是何時執行的呢?
開始個人想法是若是沒有if (hint === 'string')
這一個判斷的時候,是否是就會執行"default"
了呢?
因而我把if (hint === 'string')
和'number'
這兩個判斷的內容給去掉了,發現它仍是不會執行"default"
:
var b = {
toString () {
console.log('toString')
return '1'
},
valueOf () {
console.log('valueOf')
return [1, 2]
},
[Symbol.toPrimitive] (hint) {
console.log('symbol')
// if (hint === 'string') {
// console.log('string')
// return '1'
// }
// if (hint === 'number') {
// console.log('number')
// return 1
// }
if (hint === 'default') {
console.log('default')
return 'default'
}
}
}
console.log(String(b))
console.log(Number(b))
// 'symbol'
// 'undefined'
// 'symbol'
// NaN
複製代碼
能夠看到,執行結果居然和題2.2
中那個沒有返回值的b
有點像。
因此也就是說,這個hint
它是在調用Symbol.toPrimitive
的時候就已經肯定了的,後面並不會改變。
好比String(b)
時傳的是string
,Number(b)
時傳的是number
。
而default
這個狀況,它涉及到+
運算符,在第四節中會說到。
小夥子(姑娘),據說你已經掌握Symbol.toPrimitive
了?
OK👌,讓咱們來作個題鞏固一下:
class Person {
constructor (name) {
this.name = name
}
[Symbol.toPrimitive] (hint) {
if (hint === 'default') {
console.log('default')
return 'default'
}
if (hint === 'string') {
console.log('string')
return '1'
}
if (hint === 'number') {
console.log('number')
return 1
}
}
}
let p1 = new Person('p1');
let p2 = new Person('p2');
console.log(String(p1))
console.log(Number(p2))
console.log(p1)
console.log(p2)
複製代碼
我把原來的對象,換成了如今的class
,你不用想多,其實用它生成的實例就是一個對象,且能使用Symbol.toPrimitive
。
因此這裏的結果爲:
'string'
'1'
'number'
1
Person{ name: 'p1' }
Person{ name: 'p2' }
複製代碼
注意:這裏的p一、p2
爲何是沒有表現出Symbol.toPrimitive
函數的呢?
別忘了《【何不三連】比繼承家業還要簡單的JS繼承題-封裝篇(牛刀小試)》這裏說的,定義在class
中的全部方法都至關因而定義在其原型對象上,也就是Person.prototype
上,因此這裏p一、p2
雖然是遵循Symbol.toPrimitive
,可是使用的倒是它原型鏈上的。
咱來總結一下哈。
toString、valueOf、Symbol.toPrimitive
方法,Symbol.toPrimitive
的優先級是最高的Symbol.toPrimitive
函數返回的值不是基礎數據類型(也就是原始值),就會報錯Symbol.toPrimitive
接收一個字符串參數hint
,它表示要轉換到的原始值的預期類型,一共有'number'、'string'、'default'
三種選項String()
調用時,hint
爲'string'
;使用Number()
時,hint
爲'number'
hint
參數的值從開始調用的時候就已經肯定了說實話,這回是真的有些膨脹了,如今無論是toPrimitive
的執行機制,仍是Symbol.toPrimitive
的自定義咱都給搞懂了。
上面👆整了這麼多題,你卻是給👴來點實際會考的東西啊。
好哦,其實在實際中咱們被考的比較多的可能就是用==
來比較判斷兩個不一樣類型的變量是否相等。
而全等===
的狀況比較簡單,通常不太會考,由於全等的條件就是:若是類型相等值也相等才認爲是全等,並不會涉及到類型轉換。
可是==
的狀況就相對複雜了,先給你們看幾個比較眼熟的題哈:
console.log([] == ![]) // true
console.log({} == true) // false
console.log({} == "[object Object]") // true
複製代碼
怎樣?這幾題是否是常常看到呀 😁,下面就讓咱們一個一個來看。
首先,咱們仍是得清楚幾個概念,這個是硬性規定的,不看的話咱無法繼續下去啊。
當使用==
進行比較的時候,會有如下轉換規則(判斷規則):
2 == 3
確定是爲false
的了null、undefined
,則另外一方必須爲null或者undefined
才爲true
,也就是null == undefined
爲true
或者null == null
爲true
,由於undefined
派生於null
String
,是的話則把String
轉爲Number
再來比較Boolean
,是的話則將Boolean
轉爲Number
再來比較ToNumber
的轉換形式來進行比較(實際上它的hint
是default
,也就是toPrimitive(obj, 'default')
,可是default
的轉換規則和number
很像,具體能夠看3.10
)在一些文章中,會說道:
若是其中一方爲Object,且另外一方爲String、Number或者Symbol,會將Object轉換成字符串,再進行比較
(摘自《神三元-(建議收藏)原生JS靈魂之問, 請問你能接得住幾個?(上)》中的3. == 和 ===有什麼區別?
)
這樣認爲其實也能夠,由於想一想toPrimitive(obj, 'number')
的過程:
valueOf()
方法valueOf()
方法的返回值是基本數據類型則直接返回,若不是則繼續調用toString()
toString()
的返回值是基本數據類型則返回,不然報錯。能夠看到,首先是會執行valueOf()
的,可是引用類型執行valueOf()
方法,除了日期類型,其它狀況都是返回它自己,也就是說執行完valueOf()
以後,仍是一個引用類型而且是它自己。那麼咱們是否是就能夠將valueOf()
這一步給省略掉,認爲它是直接執行toString()
的,這樣作起題來也快了不少。
(雖然能夠將它省略,可是你得知道實際是有這麼一步的,這一點咱們在題目3.6
會驗證)
爲了方便記憶,我畫了一張後面三個規則的轉換圖,接下來咱們只須要按着這張圖的轉換規則來作題就能夠了 😁。
(理解類型相同
、null、undefined
的狀況)
來點簡單的吧
console.log(1 == 1)
console.log(1 == 2)
console.log(null == 0)
console.log(null == false)
console.log(null == {})
console.log(undefined == 0)
console.log(undefined == false)
console.log(undefined == {})
console.log(null == null)
console.log(undefined == undefined)
console.log(undefined == null)
複製代碼
謹記開頭的轉換規則來作題哦 😁。
因此這裏的答案爲:
console.log(1 == 1) // true
console.log(1 == 2) // false
console.log(null == 0) // false
console.log(null == false) // false
console.log(null == {}) // false
console.log(undefined == 0) // false
console.log(undefined == false) // false
console.log(undefined == {}) // false
console.log(null == null) // true
console.log(undefined == undefined) // true
console.log(undefined == null) // true
複製代碼
能夠看到,undefined、null
除了和它自身以及對方相等以外,和其它的比較都爲false
。
(其實以前我老是覺得null == 0
或者null == false
是爲true
的,由於以前可能會使用!flag
這種方式來判斷某個值是否是truly
,固然越到後面越知道這種方式實際上是很不嚴謹的哈)
(理解一方爲String
,另外一方爲Number
的狀況)
如果這種狀況的話,會把String
轉成Number
再來比較:
console.log('11' == 11)
console.log('1a' == 11)
console.log('11n' == 11)
console.log('0x11' == 17)
console.log('false' == 0)
console.log('NaN' == NaN)
複製代碼
這裏可能會有幾個陷阱,你們要當心了。
答案:
console.log('11' == 11) // true
console.log('1a' == 11) // false
console.log('11n' == 11) // false
console.log('0x11' == 17) // true
console.log('false' == 0) // false
console.log('NaN' == NaN) // false
複製代碼
'11' == 11
沒啥問題,字符串轉爲了數字'1a'
轉爲數字以後是NaN
'11n'
轉爲數字以後也是NaN
,可能你們會當作是bigInt
類型的,可是注意了這裏是字符串'0x11'
,以0x
開頭的十六進制,因此轉換爲數字以後是17
'false'
是一個字符串哦,並非false
,因此結果是假值'NaN'
也是字符串,不過這裏要是真的NaN
的話,那也是false
,由於NaN
這個六親不認的連它本身都不全等(也就是NaN===NaN
的結果爲false
),只有用Object.is(NaN, NaN)
纔會被判斷爲true
)(理解一方爲Boolean
的狀況)
這種狀況會將Boolean
轉爲Number
來比較,而經過上篇咱們知道,Boolean
轉Number
那是至關簡單的,只有兩種狀況:
因此若是有一方爲Boolean
的時候應該會很好作吧...
console.log(true == 1)
console.log(false == 0)
console.log(true == '1')
console.log(false == '0')
console.log(true == '0')
console.log(true == 'false')
console.log(false == null)
複製代碼
是挺簡單的哈:
console.log(true == 1) // true
console.log(false == 0) // true
console.log(true == '1') // true
console.log(false == '0') // true
console.log(true == '0') // false
console.log(true == 'false') // false
console.log(false == null) // false
複製代碼
true
和false
轉爲數字就是 0
和1
true
轉爲數字爲1
,以後另外一邊是字符串1
,依靠準則三,一方爲字符串,則將這個字符串轉爲數字而後進行比較,因此結果爲1 == 1
的結果,也就是true
false
轉爲數字0
,以後後面的"0"
也被轉爲數字0
,因此結果爲true
true
被轉換爲了1
,'0'
被轉換爲了0
,因此結果爲false
true
被轉換爲了1
,"false"
被轉換爲了NaN
,因此結果爲false
null
和false
自己就是不相等的。其實這裏不知道有沒有和我同樣對true == '0'
有疑問的呢 🤔️?
由於咱們可能見過這麼一段代碼:
if ('0') {
console.log('我會被執行')
}
複製代碼
這裏if
內的內容是會被執行的,由於字符串'0'
轉換爲布爾確實是true
,那麼我就總會認爲true == '0'
是對的。
因此這裏要注意了,'0'
確實是會被轉換爲true
,也就是:
if (true) {
console.log('我會被執行')
}
複製代碼
但在這道題中是將它與true
來作比較,那麼就要遵循「有布爾先將布爾轉換爲數字」的規則。
因此其實也就是一個轉換順序的問題,true == '0'
是先執行的布爾轉數字的。
可是你不要覺得是一個寫法順序的問題 😂,也就是說就算把true
和'0'
換個位置結果也是同樣的:
console.log('0' == true) // false
複製代碼
(一方爲對象的狀況)
在第三節的開頭那裏呆呆已經說了,當一方有爲對象的時候,實際是會將對象執行相似ToNumber
操做以後再進行比較的,可是又因爲對象的valueOf()
基本都是它自己,因此咱們能夠認爲省略了這一步,不過爲了讓你們心服口服,我這裏仍是得來驗證一下:
var b = {
valueOf: function () {
console.log('b.valueOf')
return '1'
},
toString: function () {
console.log('b.toString')
return '2'
}
}
var c = {
valueOf: function () {
console.log('c.valueOf')
return {}
},
toString: function () {
console.log('c.toString')
return '2'
}
}
console.log(b == 1)
console.log(c == 2)
複製代碼
這道題中,b
的valueOf()
返回的是一個基本數據類型
c
的valueOf()
返回的是一個引用類型。
所以結果爲:
'b.valueOf'
true
'c.valueOf'
'c.toString'
true
複製代碼
因此咱們能夠獲得這張圖:
下面作兩道題讓咱們練習一下哈。
(一方爲非數組對象的狀況)
console.log({} == true)
console.log({} == false)
console.log({} == 1)
console.log({} == '1')
console.log({} == 0)
console.log({} == Symbol(1))
console.log({} == null)
console.log({} == {})
複製代碼
哇,乍一看感受好多啊,這...我怎麼比的過來。
這時候你只要記得,有一方是Object
時,把這個Object
轉爲字符串再來比較就能夠了。
而引用類型轉字符串不知道你們還記得嗎?
分爲了數組
和非數組
兩種狀況,大體就是:
[] => ''
,['1, 2'] => '1, 2'
而後咱們再來看看上面那道題👆,{}
轉爲字符串實際上是"[object Object]"
因此能夠看出上面的執行結果全爲false
。
其中可能比較難理解的是:
{} == true
,轉換過程爲:{} == true
"[object Object]" == true // 對象轉字符串
"[object Object]" == 1 // 布爾值轉數字(準則四,一方爲布爾,轉換爲數字)
NaN == 1 // 字符串轉數字(準則三,一方爲字符串另外一方爲數字則將字符串轉數字)
// 結果爲false
複製代碼
{} == 1
,轉換過程爲:{} == 1
"[object Object]" == 1 // 對象轉字符串
NaN == 1 // 字符串轉數字(準則三,一方爲字符串另外一方爲數字則將字符串轉數字)
// 結果爲 false
複製代碼
{} == {}
: 這個你就理解爲對象是引用類型,那麼這兩個對象都有本身獨立的堆空間,確定就是不相等的了。(一方爲數組的狀況)
console.log([] == 0)
console.log([1] == 1)
console.log(['1'] == 1)
console.log([] == 1)
console.log(['1', '2'] == 1)
console.log(['1', '2'] == ['1', '2'])
console.log([{}, {}] == '[object Object],[object Object]')
console.log([] == true)
console.log([] == Symbol('1'))
複製代碼
題目解析:
console.log([] == 0)
[] == 0
'' == 0 // []空數組轉爲字符串爲空字符串
0 == 0 // 空字符串轉爲數字爲0
// true
console.log([1] == 1)
[1] == 1
'1' == 1 // [1]非空數組且數組長度爲1,轉換爲字符串爲'1'
1 == 1 // '1'字符串轉換爲數字1
// true
console.log(['1'] == 1) // 轉換過程和上面一個同樣
// true
console.log([] == 1)
[] == 1
'' == 1 // 空數組轉爲字符串爲''
0 == 1 // 空字符串轉爲數字爲0
// false
console.log(['1', '2'] == 1)
['1', '2'] == 1
'1,2' == 1 // ['1', '2']數組轉爲字符串爲'1,2'
NaN == 1 // '1,2'字符串轉爲數字爲NaN
console.log(['1', '2'] == ['1', '2']) // 引用地址不一樣
// false
console.log([{}, {}] == '[object Object][object Object]')
[{}, {}] == '[object Object][object Object]'
// [{},{}]數組中的每一項也就是{}轉爲字符串爲'[object Object]',而後進行拼接
'[object Object],[object Object]' == '[object Object],[object Object]'
// true
console.log([] == true)
[] == true
[] == 1 // 有一項爲布爾,所以將布爾true轉爲數字1
'' == 1 // 有一項爲數組, 所以將[]轉爲空字符串
0 == 1 // 空字符串轉爲數字0
// false
console.log([] == Symbol('1'))
[] == Symbol('1')
'' == Symbol('1')
// false
複製代碼
(理解!
運算符的轉換)
當咱們使用!
的時候,實際上會將!
後面的值轉換爲布爾類型來進行比較,這也就是我在題3.1
說到過的不嚴謹的狀況。
並且我發現這種轉換是不會通過ToNumber()
的,而是直接轉換爲了布爾值,讓咱們來驗證一下:
var b = {
valueOf: function () {
console.log('b.valueOf')
return '1'
},
toString: function () {
console.log('b.toString')
return '2'
}
}
console.log(!b == 1)
console.log(!b == 0)
複製代碼
這裏的執行結果是:
false
true
複製代碼
能夠看到,!b
它在轉換的過程當中並無通過valueOf
或者toString
,而是直接轉爲了false
再來作幾道題哈:
console.log(!null == !0)
console.log(!undefined == !0)
console.log(!!null == !!0)
console.log(!{} == {})
console.log(!{} == [])
console.log(!{} == [0])
複製代碼
答案:
console.log(!null == !0) // true
console.log(!undefined == !0) // true
console.log(!!null == !!0) // true
console.log(!{} == {}) // false
console.log(!{} == []) // true
console.log(!{} == [0]) // true
複製代碼
能夠看到,剛剛還不相等的null
和0
在分別加上了!
以後,就變爲相等了。
前面三個輸出結果應該都沒有什麼問題,來看看後面三個:
!{} == {}
:
!{}
,轉換以後爲false
false == {}
,一方有布爾的狀況,將布爾轉換爲數字,即0 == {}
0 == '[object Object]'
0 == NaN
false
!{} == []
:
!{}
,轉換以後爲false
false == []
,一方有布爾,將布爾轉換爲數字,即0 == []
,0 == '0'
0 == 0
true
而!{} == [0]
的轉換流程和!{} == []
同樣。
如今你能弄懂開始說的那幾道題了嗎?
讓咱們再來看看,此次確定以爲很簡單:
var b = {
valueOf() {
console.log('valueOf')
return []
},
toString () {
console.log('toString')
return false
}
}
console.log(![] == [])
console.log(![] == b)
複製代碼
![] == []
:
![]
轉換爲布爾類型,[]
爲true
,那麼![]
就是false
[]
轉爲數字是爲0
,0
與false
比較,將false
也轉換爲0
,因此結果爲true
![] == b
:
![]
轉爲了false
b
會先執行valueOf
,而後執行toString
,返回的也是false
true
答案:
true
'valueOf'
'toString'
true
複製代碼
(理解==
比較時對象的Symbol.toPrimitive
函數的hint
參數)
var b = {
[Symbol.toPrimitive] (hint) {
console.log(hint)
if (hint === 'default') {
return 2
}
}
}
console.log(b == 2)
console.log(b == '2')
複製代碼
經過上面👆幾個案例,咱們均可以看出對象在進行==
比較時會通過相似於ToNumber
的轉換過程:
valueOf()
toString()
但其在進行從新Symbol.toPrimitive
接收到的參數會是"default"
,並非"number"
。
因此這裏的答案爲:
'default'
true
'default'
true
複製代碼
(函數在使用==
時的轉換)
函數其實也是一個對象,因此在進行==
比較時也和普通對象同樣處理便可。
可是我只想要提醒一點,在進行==
比較時要注意是比較函數自己仍是比較函數的返回值。
例如在這道題中:
function f () {
var inner = function () {
return 1
}
inner.valueOf = function () {
console.log('valueOf')
return 2
}
inner.toString = function () {
console.log('toString')
return 3
}
return inner
}
console.log(f() == 1)
console.log(f()() == 1)
複製代碼
f()
表示的是inner
這個函數,因此f() == 1
至關因而inner == 1
,所以此時就涉及到了inner
函數的類型轉換,就會觸發inner.valueOf()
,返回2
,所以第一個是false
f()()
表示的是inner()
調用以後的返回值,也就是1
,因此此時是1 == 1
進行比較,並不會涉及到inner
函數的類型轉換,也就不會觸發inner.valueOf()
,所以第二個爲true
。結果:
'valueOf'
false
true
複製代碼
作完了這十一道題,相信你對==
的比較應該比以前更瞭解了吧 😁,讓咱們來總結一波。
當使用==
進行比較的時候,會有如下轉換規則(判斷規則):
2 == 3
確定是爲false
的了null、undefined
,則另外一方必須爲null或者undefined
才爲true
,也就是null == undefined
爲true
或者null == null
爲true
,由於undefined
派生於null
String
,是的話則把String
轉爲Number
再來比較Boolean
,是的話則將Boolean
轉爲Number
再來比較ToNumber
的轉換形式來進行比較(也就是toPrimitive(obj, 'defalut')
當一方有爲對象的時候,實際是會將對象執行ToNumber
操做以後再進行比較的,可是又因爲對象的valueOf()
基本都是它自己,因此咱們能夠認爲省略了這一步。
這裏我貼上一張流程圖,感受畫的挺不錯的,你們能夠對照着看一下:
(圖片來源:segmentfault.com/a/119000001…)
除了在==
的比較中會進行類型轉換以外,其它的運算符號也會有。
好比標題上常見的這五種。
這裏我主要是分兩類來講:
-、*、/、%
這四種都會把符號兩邊轉成數字來進行運算+
因爲不只是數字運算符,仍是字符串的鏈接符,因此分爲兩種狀況:(四種簡單運算符的類型轉換)
先來講說除了+
號之外的其它四種運算符的轉換,因爲基本數據類型應該都清楚,因此就不作說明了,這裏主要是想說一下對象運算時的狀況:
var b = {}
console.log(b - '2')
console.log(b * '2')
console.log(b / '2')
console.log(b % '2')
console.log(b - [])
console.log(b - {})
複製代碼
b
是一個對象,在進行這類運算的時候,把兩端都轉換爲數字進行計算,而咱們知道對象{}
轉爲數字是NaN
,因此答案全都是NaN
。
答案:
NaN
NaN
NaN
NaN
NaN
NaN
複製代碼
(四種運算符的實際轉換-重寫toString()
和valueOf()
)
咱們將上面👆那道題的b
對象重寫一下它們的toString()
和valueOf()
方法,想一想,若是它是遵循ToNumber()
轉換的話,那麼如下的結果會是什麼呢?
var b = {
valueOf () {
console.log('valueOf')
return {}
},
toString () {
console.log('toString')
return 1
}
}
console.log(b - '2')
console.log(b * '2')
console.log(b / '2')
console.log(b % '2')
console.log(b - [])
console.log(b - {})
複製代碼
在調用b
的時候,會先執行valueOf()
方法,若是該方法返回的是一個基本數據類型則返回,不然繼續調用toString()
方法,很顯然這裏的valueOf()
返回的仍是一個引用類型,因此總會調用toString()
,所以答案爲:
'valueOf'
'toString'
-1
'valueOf'
'toString'
2
'valueOf'
'toString'
0.5
'valueOf'
'toString'
1
'valueOf'
'toString'
1
'valueOf'
'toString'
NaN
複製代碼
這裏要說一下的是最後兩個輸出結果。
b - []
:
b
輸出的是1
,由於[]
轉換爲數字咱們知道是0
,因此結果爲1
b - {}
b
爲1
,{}
轉爲數字爲NaN
,因此結果爲NaN
(四種運算符的實際轉換-重寫Symbol.toPrimitive
)
4.1
那道題咱們除了重寫toString
和valueOf
咱們還能夠從新什麼呢?
嘻嘻,怎麼能忘了Symbol.toPrimitive
,若是從新了它,那你以爲它接收到的hint
參數會是什麼呢?
var b = {
[Symbol.toPrimitive] (hint) {
if (hint === 'default') {
console.log('default')
return 'default'
}
if (hint === 'number') {
console.log('number')
return 1
}
if (hint === 'string') {
console.log('string')
return '2'
}
}
}
console.log(b - '2')
console.log(b * '2')
console.log(b / '2')
console.log(b % '2')
console.log(b - [])
console.log(b - {})
複製代碼
既然是會把運算符兩邊都轉換爲數字進行計算,那麼hint
接收到的確定就是'number'
了呀,沒錯,因此這裏b
老是會返回1
,所以答案爲:
'number'
-1
'number'
2
'number'
0.5
'number'
1
'number'
1
'number'
NaN
複製代碼
yeah~感受沒啥難度。
(+
號對於對象的轉換)
+b
的狀況就至關於轉爲數字+
號兩邊有值則判斷兩邊值的類型,若兩邊都爲數字則進行數字計算,如有一邊是字符串,就會把另外一邊也轉換爲字符串進行鏈接var b = {}
console.log(+b)
console.log(b + 1)
console.log(1 + b)
console.log(b + '')
複製代碼
依照着這個規則,咱們能夠得出答案:
NaN
'[object Object]1'
'1[object Object]'
'[object Object]'
複製代碼
('+'
運算符與String()
的區別)
一樣的,咱們給上題加上Symbol.toPrimitive
看一下:
var b = {
[Symbol.toPrimitive] (hint) {
if (hint === 'default') {
console.log('default')
return '我是默認'
}
if (hint === 'number') {
console.log('number')
return 1
}
if (hint === 'string') {
console.log('string')
return '2'
}
}
}
console.log(+b)
console.log(b + 1)
console.log(1 + b)
console.log(b + '')
console.log(String(b))
複製代碼
由於+b
走的是轉換數字的路線,因此它的hint
確定就是number
。
但是對於b + 1
這種字符串鏈接的狀況,走的卻不是string
,而是default
。
因此能夠看到答案爲:
var b = {
[Symbol.toPrimitive] (hint) {
if (hint === 'default') {
console.log('default')
return '我是默認'
}
if (hint === 'number') {
console.log('number')
return 1
}
if (hint === 'string') {
console.log('string')
return '2'
}
}
}
console.log(+b) // number
console.log(b + 1) // default
console.log(1 + b) // default
console.log(b + '') // default
console.log(String(b)) // string
'number'
1
'default'
'我是默認1'
'default'
'1我是默認'
'default'
'我是默認'
'string'
'2'
複製代碼
能夠看到b + 1
和String(b)
這兩種促發的轉換規則是不同的
{} + 1
字符串鏈接時hint
爲default
String({})
時hint
爲string
鑑於我不知道上面👆的default
和number、string
有什麼區別,因此我以爲應該要重寫一下toString
和valueOf()
來看看會發生什麼。
var b = {
valueOf () {
console.log('valueOf')
return {}
},
toString () {
console.log('toString')
return 1
},
}
console.log(+b) // number
console.log(b + 1) // default
console.log(String(b)) // string
複製代碼
此時的結果爲:
'valueOf'
'toString'
1
'valueOf'
'toString'
2
'toString'
'1'
複製代碼
我發現default
的轉換方式和number
很像,都是先執行判斷有沒有valueOf
,有的話執行valueOf
,而後判斷valueof
後的返回值,如果是引用類型則繼續執行toString
。(這點其實在題目3.10
中也說到了)
(日期對象的數據轉換)
以前咱們有提到過,日期對象的轉換比較特殊。(在引用類型調用valueOf()中)
valueOf
返回的是它自己,也就是引用類型valueOf
返回的是一個數字類型的毫秒數var date = new Date()
console.log(date.valueOf())
console.log(date.toString())
console.log(+date)
console.log('' + date)
複製代碼
因此咱們能夠看到這裏的答案是:
var date = new Date()
console.log(date.valueOf()) // 1585742078284
console.log(date.toString()) // Wed Apr 01 2020 19:54:38 GMT+0800 (中國標準時間)
console.log(+date) // 1585742078284
console.log('' + date) // Wed Apr 01 2020 19:54:38 GMT+0800 (中國標準時間)
複製代碼
+date
是轉換爲數字,因此結果和date.valueOf()
結果一致。
可是咱們會發現這裏的'' + date
和上面的'' + {}
就會有所不一樣了。
雖然一樣都是被轉換爲字符串,可是還記得'' + {}
的轉換順序嗎?它的轉換方式是遵循ToNumber
的,也就是會先執行valueOf()
,再執行toString()
,因爲{}.valueOf
等於它自己,是引用類型,因此會繼續執行toString()
。
而date
進行+
號字符串鏈接不會遵循這種轉換規則,而是優先調用toString()
。
對於幾種經常使用運算符的類型轉換:
-、*、/、%
這四種都會把符號兩邊轉成數字來進行運算+
因爲不只是數字運算符,仍是字符串的鏈接符,因此分爲兩種狀況:+b
這種狀況至關於轉換爲數字)對象的+
號類型轉換:
+
號字符串鏈接的時候,toPrimitive
的參數hint
是default
,可是default
的執行順序和number
同樣都是先判斷有沒有valueOf
,有的話執行valueOf
,而後判斷valueof
後的返回值,如果是引用類型則繼續執行toString
。(相似題4.5
和4.6
)+
號字符串鏈接的時候,優先調用toString()
方法。(相似題4.7
)mm...
不知道您看到如今還好不?應該還沒炸吧
鋪墊了這麼久,是時候展現正在的技術了!
下面讓咱們來作幾道綜合題檢驗一下[陰笑~]
console.log([] == [])
console.log([] == ![])
console.log({} == 1)
複製代碼
==
號兩邊都是引用類型則判斷是否爲同一引用[] == ![]
這個在3.9
中說的很詳細了{} == 1
,簡單來講兩邊都轉換爲數字,{}
轉換爲數字爲NaN
,因此結果爲false
。詳細來講:一方爲對象,將對象轉換爲字符串進行比較,即"[object Object]" == 1
;一方有字符串,將字符串轉換爲數字進行比較,即NaN == 1
,因此結果爲false
答案爲:
console.log([] == []) // false
console.log([] == ![]) // true
console.log({} == 1) // false
複製代碼
console.log({} + "" * 1)
console.log({} - [])
console.log({} + [])
console.log([2] - [] + function () {})
複製代碼
{} + "" * 1
:
"" * 1
,結果爲0
,由於""
轉換爲數字是0
{} + 0
,將{}
轉換爲字符串是"[object Object]"
,0
轉換爲字符串是"0"
"[object Object]0"
{} - []
:
-
號兩邊轉換爲數字,{}
爲NaN
,[]
爲0
,因此結果爲NaN
{} + []
:
{}
轉爲字符串爲"[object Object]"
,[]
轉爲字符串爲""
,因此結果爲"[object Object]"
[2] - [] + function () {}
:
-
號兩邊轉換爲數字分別爲2
和0
,因此[2] - []
結果爲2
2 + function () {}
,兩邊轉換爲字符串拼接爲"2function () {}"
,由於函數是會轉換爲源代碼字符串的。答案爲:
console.log({} + "" * 1) // "[object Object]0"
console.log({} - []) // NaN
console.log({} + []) // "[object Object]"
console.log([2] - [] + function () {}) // "2function () {}"
複製代碼
這道題相信你們看的不會少,除了重寫valueOf()
你還會哪些解法呢?
解法一:重寫valueOf()
這個解法是利用了:當對象在進行==
比較的時候實際是會先執行valueOf()
,如果valueOf()
的返回值是基本數據類型就返回,不然仍是引用類型的話就會繼續調用toString()
返回,而後判斷toString()
的返回值,如果返回值爲基本數據類型就返回,不然就報錯。
如今valueOf()
每次返回的是一個數字類型,因此會直接返回。
// 1
var a = {
value: 0,
valueOf () {
return ++this.value
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
複製代碼
解法二:重寫valueOf()
和toString()
var a = {
value: 0,
valueOf () {
return {}
},
toString () {
return ++this.value
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
複製代碼
原理就是解法一的原理,只不過用到了當valueOf()
的返回值是引用類型的時候會繼續調用toString()
。
這裏你甚至均可以不用重寫valueOf()
,由於除了日期對象其它對象在調用valueOf()
的時候都是返回它自己。
也就是說你也能夠這樣作:
var a = {
value: 0,
toString () {
return ++this.value
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
複製代碼
解法三:重寫Symbol.toPrimitive
想一想是否是還能夠用Symbol.toPrimitive
來解呢?
結合題3.10
咱們知道,當對象在進行==
比較的時候,Symbol.toPrimitive
接收到的參數hint
是"defalut"
,那麼咱們只須要這樣重寫:
var a = {
value: 0,
[Symbol.toPrimitive] (hint) {
if (hint === 'default') {
return ++this.value
}
}
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
複製代碼
這樣結果也是能夠的。
解法四:定義class並重寫valueOf()
固然你還能夠用class
來寫:
class A {
constructor () {
this.value = 0
}
valueOf () {
return ++this.value
}
}
var a = new A()
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
複製代碼
解法五:利用數組轉爲字符串會隱式調用join()
什麼 ? 還有別的解法嗎?並且我看解法五的題目有點沒看懂啊。
讓咱們回過頭去看看題1.3
,那裏提到了當數組在進行轉字符串的時候,調用toString()
的結果其實就是調用join
的結果。
那和這道題有什麼關係?來看看答案:
let a = [1, 2, 3]
a['join'] = function () {
return this.shift()
}
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
複製代碼
由於咱們知道,對象若是是數組的話,當咱們不重寫其toString()
方法,在轉換爲字符串類型的時候,默認實現就是將調用join()
方法的返回值做爲toString()
的返回值。
因此這裏咱們重寫了a
的join
方法,而此次重寫作了兩件事情:
a
執行a.shift()
方法,咱們知道這會影響原數組a
的,將第一項去除因此當咱們在執行a == 1
這一步的時候,因爲隱式調用了a['join']
方法,因此會執行上面👆說的那兩件事情,後面的a == 2
和a == 3
同理。
解法六:定義class繼承Array並重寫join()
對於解法五咱們一樣能夠用class
來實現
class A extends Array {
join = this.shift
}
var a = new A(1, 2, 3)
if (a == 1 && a == 2 && a == 3) {
console.log('成立')
}
複製代碼
這種寫法比較酷🆒,可是第一次看可能不太能懂。
A
這個類經過extends
繼承於Array
,這樣經過new A
建立的就是一個數組A
重寫了join
方法,join = this.shift
就至關因而join = function () { return this.shift() }
a == xxx
的時候,都會隱式調用咱們自定義的join
方法,執行和解法五同樣的操做。這道題看着和上面那道有點像,不過這裏判斷的條件是全等的。
咱們知道全等的條件:
false
,這點和==
不一樣,==
會發生隱式類型轉換而對於上面👆一題的解法咱們都是利用了==
會發生隱式類型轉換這一點,顯然若是再用它來解決這道題是不能實現的。
想一想當咱們在進行a === xxx
判斷的時候,實際上就是調用了a
這個數據而已,也就是說咱們要在調用這個數據以前,作一些事情,來達到咱們的目的。
不知道這樣說有沒有讓你想到些什麼 🤔️?或許你和呆呆同樣會想到Vue
大名鼎鼎的數據劫持 😁。
想一想在Vue2.x
中不就是利用了Object.defineProperty()
方法從新定義data
中的全部屬性,那麼在這裏咱們一樣也能夠利用它來劫持a
,修改a
變量的get
屬性。
var value = 1;
Object.defineProperty(window, "a", {
get () {
return this.value++;
}
})
if (a === 1 && a === 2 && a === 3) {
console.log('成立')
}
複製代碼
這裏實際就作了這麼幾件事情:
Object.defineProperty()
方法劫持全局變量window
上的屬性a
a
的時候將value
自增,並返回自增後的值(其實我還想着用Proxy
來進行數據劫持,代理一下window
,將它用new Proxy()
處理一下,可是對於window
對象好像沒有效果...)
解法二
怎麼辦 😂,一碰到這種題我又想到了數組...
var arr = [1, 2, 3];
Object.defineProperty(window, "a", {
get () {
return this.arr.shift()
}
})
if (a === 1 && a === 2 && a === 3) {
console.log('成立')
}
複製代碼
中了shift()
的毒...固然,這樣也是能夠實現的。
解法三
還有就是EnoYao大佬那裏看來的騷操做:
var aᅠ = 1;
var a = 2;
var ᅠa = 3;
if (aᅠ == 1 && a == 2 && ᅠa == 3) {
console.log("成立");
}
複製代碼
說來慚愧...a
的先後隱藏的字符我打不來 😂...
現須要實現如下函數:
function f () {
/* 代碼 */
}
console.log(f(1) == 1)
console.log(f(1)(2) == 3)
console.log(f(1)(2)(3) == 6)
複製代碼
首先看到這道題的時候讓我想到了題目3.11
,只不過這裏是有傳參的,而且返回值像是一個累計的過程。
也就是說會收集每次傳遞進來的參數而後進行一個累加並返回(這個很容易想到reduce
方法)。
而且f(1)(2)
這樣的寫法很像是偏應用,函數返回了一個函數。
那咱們是否是能夠在函數f
內用一個變量數組來存放參數集合,而後返回一個函數(我命名爲inner
),這個inner
函數的做用是收集傳遞進來的參數將它添加到參數集合中。
以後就和3.11
很像,在每次進行==
比較的時候,f
返回的inner
函數會進行隱式類型轉換,也就是會調用inner
的valueOf()
和toString()
方法,那咱們只須要重寫這兩個方法,並返回用reduce
累加的參數的和就能夠了。
代碼也很簡單,一塊兒來看看:
function f () {
let args = [...arguments]
var add = function () {
args.push(...arguments)
return add
}
add.valueOf = function () {
return args.reduce((cur, pre) => {
return cur + pre
})
}
return add
}
console.log(f(1) == 1)
console.log(f(1)(2) == 3)
console.log(f(1)(2)(3) == 6)
複製代碼
固然,上面👆的valueOf()
換成toString()
也是能夠的,由於咱們已經知道了,對象==
比較時類型轉換的順序其實就是先通過valueOf
再到toString
。
氣氛不要這麼凝重嘛...讓咱們最後來看道簡單有趣的題。
這道有趣的題是從LINGLONG的一篇《【js小知識】[]+ {} =?/{} +[] =?(關於加號的隱式類型轉換)》那裏看來的。
(PS: pick一波玲瓏,這位小姐姐的文章寫的都挺好的,不過熱度都不高,你們能夠支持一下呀 😁)
OK👌,來看看題目是這樣的:
在控制檯(好比瀏覽器的控制檯)輸入:
{}+[]
複製代碼
的結果會是什麼 🤔️?
咦~這道題上面不是作過了嗎(題目5.2
裏的第三個console.log()
)?
console.log({}+[]) // "[object Object]"
複製代碼
可是注意這裏的題目,是要在控制檯輸出哦。
此時我把這段代碼在控制檯輸出結果發現答案居然和預期的不同:
{}+[]
0
複製代碼
也就是說{}
被忽略了,直接執行了+[]
,結果爲0
。
知道緣由的我眼淚掉了下來,原來它和以前提到的1.toString()
有點像,也是由於JS
對於代碼解析的緣由,在控制檯或者終端中,JS
會認爲大括號{}
開頭的是一個空的代碼塊,這樣看着裏面沒有內容就會忽略它了。
因此只執行了+[]
,將其轉換爲0
。
若是咱們換個順序的話就不會有這種問題:
(圖片來源:juejin.im/post/5e6055…)
爲了證明這一點,咱們能夠把{}
當成空對象來調用一些對象的方法,看會有什麼效果:
(控制檯或者終端)
{}.toString()
複製代碼
如今的{}
依舊被認爲是代碼塊而不是一個對象,因此會報錯:
Uncaught SyntaxError: Unexpected token '.'
複製代碼
解決辦法能夠用一個()
將它擴起來:
({}).toString
複製代碼
不過這東西在實際中用的很少,我能想到的一個就是在項目中(好比我用的vue
),而後定義props
的時候,若是其中一個屬性的默認值你是想要定義爲一個空對象的話,就會用到:
props: {
target: {
type: Object,
default: () => ({})
}
}
複製代碼
整完了整完了...啊...
知識無價,支持原創。
參考文章:
你盼世界,我盼望你無bug
。這篇文章就介紹到這裏。
能夠看到,當咱們瞭解了類型轉換的原理以後發現並非太難。關鍵就是在於半懂不懂的時候最可怕。就像沒了解以前,我能很快的說出{} == 1
的結果是false
,可是如今腦殼裏會先轉上一圈:
"[object Object]" == 1
NaN == 1
,因此結果爲false
對於一些簡單的題甚至會遲鈍一下...這些都是還不熟形成的,但願你們可以多多練習早日避免呆呆
這種呆
的狀態。
用心創做,好好生活。這篇文章出了以後這段時間可能不會再出這種都是題目的文章了,作多了你們也會麻木不想看,因此後面會出一些原理性的文章,敬請期待吧,嘻嘻😁。
若是你以爲文章對你有幫助的話來個贊👍哦,謝謝啦~ 😁。
喜歡霖呆呆的小夥還但願能夠關注霖呆呆的公衆號 LinDaiDai
或者掃一掃下面的二維碼👇👇👇.
我會不定時的更新一些前端方面的知識內容以及本身的原創文章🎉
你的鼓勵就是我持續創做的主要動力 😊.
相關推薦:
《【建議星星】要就來45道Promise面試題一次爽到底(1.1w字用心整理)》
《【建議👍】再來40道this面試題酸爽繼續(1.2w字用手整理)》
《【何不三連】比繼承家業還要簡單的JS繼承題-封裝篇(牛刀小試)》