前言:
在JAVAscript這門語言中有兩個比較重要的方法。毫不夸張的說,前端小伙伴天天在用他們。熟悉JavaScript這門語言的小伙伴肯定知道Javascript中兩個非常重要的概念:原型和原型鏈。valueOf和toString兩個方法便是定義在Object原型對象身上的兩個方法。
先來打印一下這個對象:
console.log(Object.prototype)
這就意味著,在Javascript中每個對象都可以調(diào)用這兩個方法。簡單介紹了這兩上方法之后,我們再來看幾個相關(guān)的概念:包裝對象和類型轉(zhuǎn)換
包裝對象:
在Javascript中有六種數(shù)據(jù)類型(在ES6又增加了Symbol類型,本文暫不涉及):
- 數(shù)值:number
- 字符串:string
- 布爾:boolean
- undefined
- null
- 對象
其中,number、string、boolean稱為原始類型的值(primitive type),對象稱為合成類型的值。對于對象來說我們可以通過點運算符或者方括號運算符的方式來調(diào)用屬性或者方法。對于原始類型卻不能這操作,因為他們不對象類型,但在開發(fā)中我們卻經(jīng)常看到一些寫法,如:
var name = 'this is name'
console.log(name.length) // 12
對于上面代碼,name是一個string類型的,用typeof可以看出:
typeof name // string
這是為什么呢?答案就是因為Javascript為我們提供了幾個包裝對象:Number、String 、Boolean,這三個包裝對象 在一定的條件下會把原始類型的值轉(zhuǎn)成對象類型的值。
先來看一下Number:
同理,String和Boolean也是一樣的操作,小伙伴私下可以試一下把不同的值轉(zhuǎn)成String和Boolean
var num = new Number('123')
typeof num // 'object'
// 可以看出num已經(jīng)成為一個對象類型的數(shù)據(jù)
那么可以用原始類型的值調(diào)用屬性或者是方法的過程就是因為原始對象借助包裝對象自動轉(zhuǎn)成了對象,過程如下:
- 創(chuàng)建一個臨時的對象
- 調(diào)用這個對象的屬性或者方法
- 把這個臨時的對象自動銷毀
看個經(jīng)典的面試題:
var str = 'name'
str.hello = 'hello'
console.log(str.hello + ' world') // 打印出什么??
再來看一下類型轉(zhuǎn)換的概念
類型轉(zhuǎn)換:
Javascript是一種弱類型的語言,一個變量的類型不是一成不變的,有可能上行代碼是number類型,下一行代碼就成了string類型的。有可能就出現(xiàn)兩個不同類型的數(shù)據(jù)進行運算了,如:
'100' - 99 // 1
這兩個字符串類型的值,也是可以進行數(shù)值運算的,運算結(jié)果是1,這就說明在運算的時候發(fā)生的類型轉(zhuǎn)換,字符串轉(zhuǎn)成了數(shù)值,然后再進行運算:100 - 99 = 1
在處理類型轉(zhuǎn)換的時候,javascript提供了兩種方式:強制類型轉(zhuǎn)換和自動轉(zhuǎn)換
強制類型轉(zhuǎn)換:
通過Number()、String()和Boolean()三個函數(shù)來完成,咦~~怎么和前面的包裝對象差不多呢,哈哈,其實就是同一個函數(shù),當這三個函數(shù)被當成構(gòu)造函數(shù)使用的時候,即使用new關(guān)鍵字的時候,就是把原始值轉(zhuǎn)成對象;當函數(shù)作為普通函數(shù)的時候就是把原始值強制轉(zhuǎn)成所對應(yīng)的值。
自動類型轉(zhuǎn)換:
經(jīng)常遇到自動轉(zhuǎn)換的兩種情況,當然還有其它情況,這里不一一列舉了
- 運算符兩邊數(shù)據(jù)類型不一樣
99 + '1' // 991
- 非布爾值求布爾值
var str = 'abc'
if (str) {
console.log('true')
} else {
console.log('false')
}// 運算結(jié)果是:'true'
那么問題來了,自動轉(zhuǎn)換的規(guī)則是什么呢~
以Number轉(zhuǎn)換為例:對于原始類型轉(zhuǎn)有Number比較簡單,記住那幾種情況就好了。對于合成類型是怎么做的呢?這就用了前面說過的 valueOf 和 toString 兩個方法了。
當Number函數(shù)要把一個對象轉(zhuǎn)成數(shù)值的時候:
- 調(diào)用對象的valueOf方法,如果方法返回是一個原始值,則直接通過Number函數(shù)轉(zhuǎn)。不再往后判斷
- 如果valueOf方法返回的是一個對象,則繼續(xù)調(diào)用toString方法,如果返回一個原始值,則直接通過Number函數(shù)轉(zhuǎn)
- 如果valueOf和toString都返回是對象,則直接報錯
分幾種情況來看一下:
// 重寫對象的valueOf和toString方法
var obj = { valueOf: function () {
console.log('調(diào)用valueOf方法')
return 1
}, toString: function () {
console.log('調(diào)用toString方法')
return 2
},}var result = 1 + obj // 調(diào)用valueOf方法
console.log(result) // 2
// 只重寫對象的valueOf方法
var obj = { valueOf: function () {
console.log('調(diào)用valueOf方法')
return 1
}}var result = 1 + obj // 調(diào)用valueOf方法
console.log(result) // 2
// 只重寫對象的toString方法
var obj = { toString: function () {
console.log('調(diào)用toString方法')
return 2
},}var result = 1 + obj // 調(diào)用toString方法
console.log(result) // 3
前兩種情況上一樣的,說明一下第三種情況為什么是調(diào)用的toString方法:
如果沒有重寫對象的valueOf方法,那么對象就會調(diào)用原型鏈上的valueOf方法,即Object.prototype對象中方法,這個方法默認返回的是對象本身,根據(jù)前面所說的轉(zhuǎn)換規(guī)則,當調(diào)用valueOf方法的時候返回的是一個對象,會繼續(xù)調(diào)用toString方法,看看是否返回原始類型的值,所以經(jīng)過調(diào)用toString發(fā)現(xiàn)返回的是2,是一個原始類型,不再往下執(zhí)行,所以會打印出3 ,1 + 2 = 3,再來看一下兩個方法都返回對象是什么樣的:
// 重寫對象的valueOf和toString方法
var obj = { valueOf: function () {
console.log('調(diào)用valueOf方法')
return {}
}, toString: function () {
console.log('調(diào)用toString方法')
return {}
},}var result = 1 + obj
console.log(result)
//執(zhí)行結(jié)果如下:// 調(diào)用valueOf方法// 調(diào)用toString方法// Uncaught TypeError: Cannot convert object to primitive value// 先調(diào)用valueOf方法,后調(diào)用toString,最后報錯,印證了上面我們所說的結(jié)論:當兩個方法都返回對象的時候,會報錯
最后說一下兩個方法的調(diào)用情況:
- valueOf偏向值運算,當有運算符的時候,會先調(diào)用此方法
- toString偏向顯示,當用alert函數(shù)的時候,會先調(diào)用此方法
關(guān)注我,或者公眾號`知碼前端`分享更多前端知識~~






