记一道题

题目

1
2
3
4
5
- 下面代码中 a 在什么情况下会打印 1 ?
var a = ?;
if (a == 1 && a == 2 && a == 3) {
console.log(1);
}

同事老哥分享在群里的一道题目,题目来源于Daily-Interview-Question

我看到题目二话没说来了一句var a = (function() {console.log(1)})();,直接把老哥们皮出了一阵省略号…

其实这题考察的应该是==的隐式转换, 当引用类型在比较运算符时,隐式转换会调用本类型toString或valueOf方法.

重写toString

1
2
3
4
5
6
7
8
9
10
11
12
var a = {
num: 1,
toString() {
return ++this.num;
}
};

if (a == 1 && a == 2 && a == 3) {
console.log(1);
}

=> 1

重写valueOf

1
2
3
4
5
6
7
8
9
10
11
12
var a = {
num: 1,
valueOf() {
return ++this.num;
}
};

if (a == 1 && a == 2 && a == 3) {
console.log(1);
}

=> 1

总体思路就是当 a 获取一次值的时候返回值+1, 让其符合 if 条件语句从而打印 a

那么我们还可以通过defineProperty重写对象的get方法实现

defineProperty

1
2
3
4
5
6
7
8
var num = 1;
Object.defineProperty(window, 'a', { get: function() { return ++num } });

if (a == 1 && a == 2 && a == 3) {
console.log(1);
}

=> 1

我们除了通过defineProperty实现之外还可以通过Proxy实现, 这也正是Vue2.0和3.0数据绑定实现的区别

Proxy

1
2
3
4
5
6
7
8
9
10
11
12
var _a = 0;
let a = new Proxy({}, {
get: function() {
return ++_a;
}
})

if (a == 1 && a == 2 && a == 3) {
console.log(1);
}

=> 1

秀儿

除了以上答案等实现方法外,还有个方法实现得很秀

1
2
3
4
5
6
7
8
let a = [1,2,3];
a.join = a.shift;

if( a == 1 && a == 2 && a == 3 ) {
console.log(1);
}

=> 1

a 赋值为数组[1,2,3],然后将Array.shift方法重写Array.join 方法

1
2
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
join() 方法用于把数组中的所有元素放入一个字符串。

讲道理, 不是应该重写Array.toString方法,我将a.join = a.shift;改成a.toString = a.shift;运行

果然, 控制台打印了1.

后来上谷歌查询ECMA规范22.1.3.27处 , Array.toString方法果然是调用Array.join方法

1
2
3
4
5
6
7
8
9
10
11
12
22.1.3.27 Array.prototype.toString ( )
When the toString method is called, the following steps are taken:

1. Let array be ToObject(this value).
2. ReturnIfAbrupt(array).
3. Let func be Get(array, "join").
4. ReturnIfAbrupt(func).
5. If IsCallable(func) is false, let func be the intrinsic function %ObjProto_toString% (19.1.3.6).
6. Return Call(func, array).

NOTE
The toString function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method.

学习了, 给出这个答案的人真的非常厉害 ! ! !

总结

一个人知识面就像是一个圆, 这个圆的边界即是你所未知的. 当你知道得越多的同时, 你所不知道的也就越多.

路漫漫其修远兮, 吾将上下而求索.