由一道面试题引入的对JavaScript隐式转换的学习

297 阅读5分钟

2022/1/7更新

神奇的一题,记录一下——

%)PEOPU@LJT23WW0SDK}$UN.jpg

输出是—— tostr tostr true

历程如下:

【1】➖优先执行 等号右边相当于 Number(obj)-1=1(同时发生隐式类型转换 b自减)-1=0

【2】之后执行等号左边与等号右边的判等操作 相当于Number(obj)==0相当于0(同时发生隐式类型转换 b自减)==0结果自然为true

【3】发生了隐式类型转换 b=-1

1.来自某四字大厂的一道面试题

前段时间网上冲浪,看到一道很奇怪的面试题

if ([] == false) {
    console.log(1);// 1
}
if ([]) {
    console.log(3);// 3
}
if ({} == false ) {
    console.log(2);// undefined 
}
if ({}) {
    console.log(2);// 2
}
if ([1] == [1]) {
    console.log(4);// undefined 
}

第一眼我是懵的,后来再一想,嗷,隐式转换

再转念一想,现在哪儿还有==啊,这都是大鹏的瓜,不都用===嘛?

再再转念一想,基础知识这东西,面试他要用啊。那还等啥,学吧。

2.隐式转换基础知识

读一下红宝书~复习下隐式类型转换的基础知识。

红宝书原文

篇幅并不长,但是十分全面,下面来好好分析下~

image.png

对操作数进行类型转换时的规则

a == b;// == 两边的操作数a b进行类型转换时遵循如下规则:
  • a为布尔值则将其转换为数值再与b比较,false 转换为 0,true 转换 为 1

image.png

  • a为字符串,b为数值,则将字符串转换为数值,再与b进行比较

image.png

  • a为对象,b不是对象,则调用valueOf()方法取得原始值,然后再根据前面的规则进行比较(如果valueOf方法返回的值不是原始值,就调用toString方法)

image.png

进行比较时操作符遵循的规则

  • null == undefined

  • null & undefined 不能转换为其他类型的值再进行比较 image.png

  • 有任一操作数为 NaN 则相等操作符会返回true

image.png

  • 如果两个操作数a和b都是对象,则需要比较它们是不是同一个对象,如果都指向同一个对象,则相等操作符返回true,否则不相等。

image.png

3.面试题解析

看完基础知识,来解析下最上面那道题

【1/3】

if ([] == false) {
    console.log(1);// 1
}
if ({} == false ) {
    console.log(2);// undefined 
}
  • [] 与 false 都会转换为0

image.png

  • {}会被转换为NaN 所以不相等

image.png

【2/4】这里很明显不是强制类型转换的内容,而是if语句条件的问题

if ([]) {
    console.log(3);// 3
}
if ({}) {
    console.log(2);// 2
}

来看看红宝书怎么说:

image.png

image.png

这样就能解释得通这两题了!

【5】

if ([1] == [1]) {
    console.log(4);// undefined 
}

就很简单了~上面的基础知识有提到这一点 image.png

两个对象的地址值是不同的~

4.写在后面

本文的思考只是针对上面提到的这个面试题,还有很多偏难怪的问法没有涉及到,建议去多看一些其他的题来保证在面试中不会被问倒!

另外我在面试中碰到的隐式转换相关问题也会及时总结进来!保证知识的新鲜程度😂

这里列举几篇我看到的非常出色的相关文章,里面题目会丰富/全面很多,大家可以加加餐——

感兴趣的朋友们可以点开前两篇读来看看(第三篇题很多,但是总结得不太系统),感受下JavaScript的这个“坑”。然后再去看看第三篇的题是否都可以理解🧐

再说一个有趣的点——

上面这三篇文章都不约而同地提到了同一道面试题

超级高频强制类型转换面试题

可见其重要程度!

var a = {
    i:1,
    valueOf:function(){
        return a.i++;// this.i++;
    }
}
if(a == 1 && a == 2 && a == 3){
    console.log("每次进行相等运算都会调用一次a(对象)的valueOf()方法,符合前面提到的转换规则!")
}

image.png

再来一题(同理)

var a = {
    arr: [1, 666],
    i: 0,
    valueOf:function(){
        return this.arr[this.i++];
    }
}
console.log(a == 1 && a == 666);// true

21/11/18 来拓展一个很类似的

参考链接 数据劫持|数据代理

什么样的 a 可以满足 (a === 1 && a === 2 && a === 3) === true 呢?(注意是 3 个 =,也就是严格相等)???

这就跟隐式类型转换无关了——这个是使用 Object.defineProperty()(Vue2.x同样用这个方式实现双向绑定,Vue3.x使用的是ES2016中的代理Proxy) 完成数据劫持的操作!

Object.defineProperty(window, 'a', {
  get () {
    current++
    return current
  }
})
console.log(a === 1 && a === 2 && a === 3) // true

说了这么多,来总结一下对象的强制类型转换流程!

对象的强制类型转换流程总结

【1】调用valueOf()方法,是原始值类型就返回,不是就继续下一步

【2】调用toString()方式,是原始值类型就返回,不是就继续下一步

【3】调用Number,是原始值就返回,不是就报类型错误(也只有undefined大神会这样了😂Number(undefined);//NaN Number(null);//0 )

举个例子

var b = {
    toString:function(){
        return this.i = 10;// this.i++;
    }
}
if(b == 10){
    console.log("只有valueOf()返回值不是原始值类型的时候才会动用toString方法")
}

image.png 好吧这个是我总结的来着,红宝书上没有写,但是我认为理应如此,证据如下:

image.png

可以看到 [] 经过隐式类型转换是与 0 相等的

就是因为最后这一步 Number()!