这是我参与8月更文挑战的第二天,活动详情查看:8月更文挑战
前言
如果对js的类型做简化,可以分为两种类型,分别是引用类型和值类型,Object和Function是引用类型,其他是值类型。
正文
从值类型到引用类型
可以通过Object()或者new Object()
当以非构造函数形式被调用时,
Object的行为等同于new Object()。 MDN解释如下:
根据上述我们可以得出以下结论:
let o = Object(1)
// 结果为true
console.log(Object.getPrototypeOf(o) == Number.prototype)
let a = {}
o = Object(a)
// 结果为 true
console.log(Object.getPrototypeof(o) == a)
可以看出来,从值到引用的转换比较简单,只需通过Object就可以得到预期的引用对象。
引用类型到值类型
引用转换为值,涉及到三个函数,分别是toString,valueOf, [Symbol.toPrimitive]
toString
返回一个表示该对象的字符串
当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用
valueOf
返回指定对象的原始值
JavaScript调用
valueOf方法将对象转换为原始值。你很少需要自己调用valueOf方法;当遇到要预期的原始值的对象时,JavaScript会自动调用它。 常见的返回值
- Array 返回数组对象本身
- Object 对象本身
- Function 函数本身
- String 字符串
如果对象没有原始值,则
valueOf将返回对象本身
Symbol.toPrimitive
Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。
该函数被调用时,会被传递一个字符串参数
hint,表示要转换到的原始值的预期类型。hint参数的取值是"number"、"string"和"default"中的任意一个 返回值是原始数据, (string,number,bigint,boolean,null,undefined,symbol)
举个例子
const object = {
[Symbol.toPrimitive](hint) {
console.log('hint', hint)
if (hint === 'number') {
return 0;
} else if (hint === 'string') {
return 'string'
} else {
return '1'
}
}
};
// 输出hint为default
console.log(object + 2);
// 输出hint为number
console.log(object - 2)
// 输出hint为string
console.log(`${obj}`)
};
过程
变量x在运算时的转换过程
- 如果x是原始值,直接返回x
- 如果x是对象(Number, String, Boolean),则通过
valueOf返回原始值 - 如果对象没有原始值,就需要进行类型转换,在js中,如果一个运算无法确定类型,那么在类型转换前,它的运算数将被预设为
number,对应上述toPrimitive的hint。当hint是number时,优先调用valueOf方法;否则优先调用toString。并且,当先调用的方法得不到非对象值时,还会调用另一个方法。 举一些例子
const toString = Number.prototype.toString
const valueOf = Number.prototype.valueOf
Number.prototype.toString = function() {
console.log('toString')
return toString.call(this)
}
Number.prototype.valueOf = function() {
console.log('valueOf')
return valueOf.call(this)
}
let o = Object(Number(1))
o[Symbol.toPrimitive]= function(hint) {
console.log('toPrimitive', hint)
}
输出结果
执行${o}时,预期的hint是string, 所以优先执行toString。
// 打印 valueOf
console.log(o - 2)
// 打印 valueOf
console.log(o + 2)
// 打印 toString
console.log(`${o}`)
改成Object
const toString = Object.prototype.toString
const valueOf = Object.prototype.valueOf
Object.prototype.toString = function() {
console.log('toString')
return toString.call(this)
}
Object.prototype.valueOf = function() {
console.log('valueOf')
return valueOf.call(this)
}
let o = {a: 2}
结果如下
前两项预期都是number,所以先执行valueOf, 但是返回的是对象,所有再执行toString
// 先打印valueOf
// 再打印toString()
console.log(+o)
// 先打印valueOf
// 再打印toString()
console.log(o - 2)
// 打印toString()
console.log(`${o}`)
如果在上述的对象加上 Symbol.toPrimitive,会发现valueOf和toString不会再执行,这是因为默认的Symbol.toPrimitive会根据hint去调用valueOf和toString,被重写之后,自然不会再调用。
o[Symbol.toPrimitive]= function(hint) {
console.log('toPrimitive', hint)
}
上面讲的就是js的类型转换过程,通过这些知识就可以理解某些情况下,x == x 不为true的原理了。