说明
在网上看到的一个面试题:什么时候 a == 1 && a == 2 && a == 3
为 true?
觉得很有趣,记录一下。(当然我想不出来_(:3」∠)_
).
分析
关于要这个表达式成立, a == 1 && a == 2 && a == 3
的每一个条件都要为 true
.
1、显然,如果 每次调用 变量 a ,a 的 值都固定不变,肯定没戏。
2、所以,要想使 a == 1、a == 2、a == 3 都为 true,就是只能每次调用 a 时,a 的 值每次 加 1。
注意:
当两个类型不同时进行 == 比较时,会将一个类型转为另一个类型,然后再进行比较。
1、Object类型与Number类型进行比较时,Object类型会转换为Number类型。
对象转换为 Number 时,会尝试调用 Object.valueOf()和Object.toString()来获取对应的数字基本类型。
2、数组调用toString()会隐含调用Array.join()方法。
解法一:对象类型转换
1、覆盖自定义对象的 valueOf() 方法
Object.prototype.valueOf()-参考文档
Object 的 valueOf() 方法会返回指定对象的原始值。
不同类型对象的valueOf()方法的返回值。
对象 | 返回值 |
Array | 返回数组对象本身。 |
Boolean | 布尔值。 |
Date | 存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。 |
Function | 函数本身。 |
Number | 数字值。 |
Object | 对象本身。这是默认情况。 |
String | 字符串值。 |
Math 和 Error 对象没有 valueOf 方法。 |
代码实现:
var val = 0 var a = { valueOf: function() { console.log('valueOf-->', val) return ++val } } // 分开打印 console.log('Number-->', Number(a)) console.log('Number-->', Number(a)) console.log('Number-->', Number(a)) /* 输出结果: valueOf--> 0 Number--> 1 valueOf--> 1 Number--> 2 valueOf--> 2 Number--> 3 */ // 或者直接打印 console.log(a == 1 && a == 2 && a == 3) // 输出:true
2、覆盖自定义对象的 toString() 方法
代码实现:(跟上面的有点类似)
var a = { i: 1, toString: function() { console.log('toString-->', a.i) return a.i++ } } // 分开打印 console.log('Number-->', Number(a)) console.log('Number-->', Number(a)) console.log('Number-->', Number(a)) /* 输出结果: toString--> 1 Number--> 1 toString--> 2 Number--> 2 toString--> 3 Number--> 3 */ // 或者直接打印 console.log(a == 1 && a == 2 && a == 3) // 输出:true
代码分析:
object --> number (引用类型转换为原始类型)
1、先调用object的valueOf方法,如果为原始值,则return,否则第2步
2、调用object的toString方法,如果为原始值,则return,否则第3步
3、抛出TypeError异常
如果到这里还是不怎么清晰的话:(请看下图:里面涉及ToPrimitive函数)
由Dry同学整理提供下图
解法二:数组类型转换
var a = [1, 2, 3]; a.join = a.shift; // 核心操作转移 console.log(a == 1 && a == 2 && a == 3); // valueOf --> toString --> join
代码分析:
上面代码 a.join = a.shift;
数组 a 的 join 被 shift 方法覆盖。
而数组调用 toString()
会隐含调用 Array.join()
方法。
从而达到一个目的:对数组 [1, 2, 3]
执行 shift
方法操作。
解法三:创建属性 defineProperty 的 get
var val = 0; Object.defineProperty(window, 'a', { get: function() { return ++val; } }); console.log(a == 1 && a == 2 && a == 3);