toString引发的数据类型转换总结分析

90 阅读3分钟

本文旨在总结 JavaScript数据类型转换方面的知识点

  1. toString转换的结果
  2. valueOf转换的结果
  3. 隐式转换/显示转换
  4. 转换机制
  5. 实战操作

一.Object原型有toString,子类型对toString有重写,示例:

talk is cheap show me your code!!!

1.Object.prototype.toString.call()
Object.prototype.toString.call(false)                // [object Boolean] 
Object.prototype.toString.call(123)                  // [object Number] 
Object.prototype.toString.call('abc')                // [object String] 
Object.prototype.toString.call({})                   // [object Object] 
Object.prototype.toString.call([])                   // [object Array] 
Object.prototype.toString.call(new Date())           // [object Date] 
Object.prototype.toString.call(new RegExp(/[a-b]/))  // [object RegExp] 
Object.prototype.toString.call(undefined)            // [object Undefined] 
Object.prototype.toString.call()                     // [object Undefined] 
Object.prototype.toString.call(null)                 // [object Null] 
2.Array.prototype.toString.call()

Array的重写有两点不同,首先是数组类型返回 '1,2,3' 格式的字符串,其次是传入undefined和null会报错异常,传入其他类型与 Object一致

Array.prototype.toString.call(false)                // [object Boolean] 
Array.prototype.toString.call(123)                  // [object Number] 
Array.prototype.toString.call('abc')                // [object String] 
Array.prototype.toString.call({})                   // [object Object] 
Array.prototype.toString.call([1,2,3])              // *** '1,2,3'
Array.prototype.toString.call(new Date())           // [object Date] 
Array.prototype.toString.call(new RegExp(/[a-b]/))   // [object RegExp] 
Array.prototype.toString.call(undefined)            // *** error
Array.prototype.toString.call()                     // *** error
Array.prototype.toString.call(null)                 // *** error

除Array和Object类型外,其余类型不能用在非自身子类型的变量上,否则报错异常

3.包装类型:Number.prototype.toString.call()
Number.prototype.toString.call()                    // *** error
Number.prototype.toString.call(123)                 // 123
4.包装类型:String.prototype.toString.call()
String.prototype.toString.call()                    // *** error
String.prototype.toString.call('123')               // '123'
5.Date.prototype.toString.call()
Date.prototype.toString.call()                    // *** error
Date.prototype.toString.call(new Date())          // 'Sun Sep 11 2022 05:59:39 GMT+0800 (中国标准时间)'
6.RegExp.prototype.toString.call()
RegExp.prototype.toString.call()                    // *** error
RegExp.prototype.toString.call(new RegExp(/[a-z]/)) // '/[a-z]/'
7.Function.prototype.toString.call()
Function.prototype.toString.call()                    // *** error
Function.prototype.toString.call(function() {})       // 'function() {}'

二.toString.call()等同Object.prototype.toString.call()

toString.call(false)                // [object Boolean] 
toString.call(123)                  // [object Number] 
toString.call('abc')                // [object String] 
toString.call({})                   // [object Object] 
toString.call([])                   // [object Array] 
toString.call(new Date())           // [object Date] 
toString.call(new RegExp(/[a-b]/))  // [object RegExp] 
toString.call(undefined)            // [object Undefined] 
toString.call()                     // [object Undefined] 
toString.call(null)                 // [object Null] 

三.隐式转换&&运算符

// 基本类型在操作属性或调用方法时,会隐式用包装类处理,操作之后删除包装类
let a = 1 
a.len = 4
console.log(a.len)                  // undefined
// 运算时根据[Symbol.toPrimitive]判断先调用valueOf再调用toString,还是先调用toString,再调用valueOf
console.log(new Date()+',hello')    // Sun Sep 11 2022 05:59:39 GMT+0800 (中国标准时间),hello
console.log(new Date()-10)          // 1662867529712
console.log([2,3,4]+',hello')       // 2,3,4,hello
console.log({} > 1)                 // false
console.log(['a'] == 'a')           // true

四.valueOf

直接调用

(false).valueOf()                // false
(123).valueOf()                  // 123
('abc').valueOf()                // 'abc'
({}).valueOf()                   // {}
([]).valueOf()                   // []
(new Date()).valueOf()           // 1662878551856
(new RegExp(/[a-b]/)).valueOf()  // /[a-b]/
(undefined).valueOf()            // error
valueOf()                        // error
(null).valueOf()                 // error

Object.valueOf返回的是对象

Object.prototype.valueOf.call(false)                // [Boolean: false]
Object.prototype.valueOf.call(123)                  // [Number: 123]
Object.prototype.valueOf.call('abc')                // [String: 'abc']
Object.prototype.valueOf.call({})                   // {} 
Object.prototype.valueOf.call([])                   // []
Object.prototype.valueOf.call(new Date())           // 2022-09-11T03:45:44.314Z
Object.prototype.valueOf.call(new RegExp(/[a-b]/))  // /[a-b]/
Object.prototype.valueOf.call(undefined)            // error 
Object.prototype.valueOf.call()                     // error 
Object.prototype.valueOf.call(null)                 // error

五.显示转换

String
// 调用变量自身类型toString->Object原型上toString->valueOf
console.log(String({name:1}))       // [object Object]
Number
console.log(Number([1,2]))          // NaN

六.[Symbol.toPrimitive]

MDN:Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数

引用类型转换时先会调用symbol.toPrimitive方法,参数hint会得到三种不同情况:'string','number','default'判断先调用toString还是valueOf

Array.prototype[Symbol.toPrimitive] = function(hint) {
    if(hint == 'string') {
        return this.toString() instanceof Object ? : this.valueOf() : this.toString()
    }else {
        return this.valueOf() instanceof Object ? : this.toString() : this.valueOf()
    }
}
Array.prototype.toString = function(hint) {
    console.log('toString')
    return this.join(',')
}
Array.prototype.valueOf = function(hint) {
    console.log('valueOf')
    return this
}
a=[1,2]
// hint == ‘string'情况
console.log(`${a}`)
console.log(String(a))
// hint == ‘default'情况,无法确定是string还是number时是default
console.log(a+123)
console.log(a==132)
// hint == ‘number'情况
console.log(+a)
console.log(a-1)
console.log(a>1)
console.log(a*1)

hint==string则会优先调用toString转换,如果返回的是引用数据类型则继续调用valueof hint==default||hint==number则会优先调用valueOf转换,如果返回的是引用数据类型则继续调用toString

七.实战

1)进制转换,生成随机验证码

// 生成随机数,按照36(26字母+10数字)进制转换成字符串,截取字符串
Math.random().toString(36).slice(3,7)

2)实现累加函数

let arr = []
function add(...args){
    arr = arr.concat(args)
    return add
}
add.toString = function(){
    return arr.reduce((a,b)=>a+b,0)
}
console.log(+add(1,2)(3))              // 6

3)实现a==1&&a==2&&a==3

let value = 1
class A {
    [Symbol.toPrimitive](){
        return value++
    }
}
let a = new A()
console.log(a==1&&a==2&&a==3)          // true