这道面试题涉及的知识点有点多,主要就是想考察你的js功底
实现的方法一般要重写对象的 valueOf() 方法 或者 toString() 方法
例如:
const a = {
value: 1,
toString() {
return this.value++;
}
}
console.log(a == 1 && a == 2 && a == 3) // true
首先搞懂=,==和===
- 单等号
=常用于赋值操作,这个不再过多讲了 - 双等号
==是等于操作符,如果两边的值相等就会返回true,因为js中存在类型隐式转换机制,所以使用==时会先进行类型转换,然后在进行比较。
console.log("2" == 2) //true
console.log(true == 1) //true
console.log(null == undifined) //true
- 三等号
===恒等操作符,与双等号不同的是,三等号不会进行类型转换,也就是说三等号两边的数据,必须类型相同,值相同,才会返回true
console.log(true === 1) //false
console.log("2" === 2) //false
console.log(null === undifined) //false
搞懂双等号==过程中的的隐式类型转换机制
当使用==比较时,如果一个运算对象是原始值,而另一个运算对象是对象(object),则该对象将转换为没有首选类型的原始值。该过程中会调用引用类型值原型上的 valueOf() 和 toString() 方法,将其转化为原始类型。这两个方法的返回值会根据需要进行转换。具体的调用顺序如下:
- 如果运算对象不是字符串,则先尝试调用
valueOf(): 如果对象具有valueOf()方法,运算对象不是字符串,且返回的值是原始值(非对象),则直接使用该值进行比较。 - 没有
valueOf(): 如果对象没有valueOf()方法,或者valueOf()返回的是对象,那么 JavaScript 将调用对象的toString()方法。 - 如果运算对象是字符串或者没有
valueOf(),则先调用toString(): 如果对象具有toString()方法且返回的值是原始值(非对象),则直接使用该值进行比较。 - 没有
toString()如果对象没有toString()方法,或者toString()返回的是对象,则会抛出TypeError错误。
Object.prototype.valueOf()
MDN上描述:valueOf 方法将对象转换成基本类型值。你很少需要自己调用 valueOf 方法;当遇到需要基本类型值的对象时,JavaScript 会自动的调用该方法。强制数字类型转换和强制基本类型转换优先会调用该方法,而强制字符串转换会优先调用 toString(),
基本的 valueOf() 方法返回 this 值本身,如果尚未转换为对象,则转换成对象。因此,任何基本类型转换算法都不会使用它的返回值。
const a = {
value: 1,
};
console.log(a.valueOf()); // {value: 1}
所以使用 == 与数值进行比较时,会优先调用 Object.prototype.valueOf()方法,而这个方法是返回对象本身,所以我们可以重写 Object.prototype.valueOf() 方法,改变其返回值:
const a = {
value: 1,
valueOf() {
return this.value;
},
};
console.log(a.valueOf()); // 1
利用闭包的特性:
const a = {
value: 1,
valueOf() {
return this.value++;
},
};
console.log(a.valueOf()); // 1
console.log(a.valueOf()); // 2
console.log(a.valueOf()); // 3
回到下面这个代码中,因为使用==类型转换会默认调用 Object.prototype.valueOf()方法,所以这就很容易明白了
const a = {
value: 1,
valueOf() {
return this.value++;
}
}
console.log(a == 1 && a == 2 && a == 3) // true
console.log(a.valueOf() == 1 && a.valueOf() == 2 && a.valueOf() == 3) // true
Object.prototype.toString()
MDN上描述:调用 toString 方法将对象转换为一个原始值。你很少需要自己去调用 toString 方法;当遇到需要原始值的对象时,JavaScript 会自己调用它。强制数字类型转换和强制基本类型转换优先会调用 valueOf 方法,而强制字符串转换会优先调用 toString(),并且 toString() 很可能返回字符串值(甚至对于 Object.prototype.toString() 基本实现也是如此),因此在这种情况下不会调用 valueOf()。
所以重写 Object.prototype.toString() 也会实现上面的 valueOf 效果。因为我们没有重写 valueOf,而 valueOf 返回的是对象本身,将会默认继续调用 toString 方法。
const a = {
value: 1,
toString() {
return this.value++;
}
};
console.log(a.toString()); // 1
console.log(a.toString()); // 2
console.log(a.toString()); // 3
const a = {
value: 1,
toString() {
return this.value++;
}
};
console.log(a == 1 && a == 2 && a == 3) // true