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