闲下来的时候刷到了一个贼坑的面试题,感觉还挺有意思的,结果就打脸了,感觉自己js基础知识白学了。。
很坑的面试题
题目是这样的
javascript
var val = ++[[]][+[]]+[+[]];
console.log(val) // val结果是啥
看到这道题,我心有一万头草泥马奔腾而过,还能这么玩呀,摸不着头脑,一直没有解题思路,既然这样,那就让我们策马奔腾,共享前端繁华吧。
我默默打开了控制台,看了下最终结果是 "10"
思考一下,这道面试题在考察什么?
仔细想想,就是js里的 类型转换规则
和 运算规则
,我们来先回顾一下这两个知识点吧。
类型转换规则
原始类型转数字
原始值 | 结果 |
---|---|
true | 1 |
false | 0 |
null | 0 |
undefined | NaN |
string | 空字符串(含空白字符) => 0 去掉引号,忽略前后空格,不是数字就是NaN |
javascript
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number("") // 0
Number(" ") // 0
Number("123") // 123
Number(" 123 ") // 123
Number("123a") // NaN
Number(" 12 3 ") // NaN(特殊)
所有类型转布尔
原始值 | 结果 |
---|---|
null | false |
undefined | false |
number | 0 => false 其余 => true |
string | 空字符串 => false 其余 => true |
对象或函数 | true |
javascript
Boolean(null) // false
Boolean(undefined) // false
Boolean(0) // false
Boolean(1) // true
Boolean(-1) // true
Boolean("") // false
Boolean(" ") // true
Boolean(" 1 2 3 ") // true
Boolean([]) // true
Boolean({}) // true
Boolean(() => {}) // true
原始类型转字符串
原始值 | 结果 |
---|---|
null | "null" |
undefined | "undefined" |
number | "数字" |
boolean | true => "true" false => "false" |
javascript
String(null) // "null"
String(undefined) // "undefined"
String(0) // "0"
String(1) // "1"
String(-1) // "-1"
对象转原始
对象转为原始类型分两步
- 调用对象的
valueOf
方法,返回如果是对象,则进行第二步,否则结束 - 调用对象的
toString
方法,返回如果还是对象,则报错
javascript
([]).valueOf() // []
([]).toString() // ""
([1,[2]]).toSting() // "1,2" 遍历递归枚举数组的每一项的toString()的结果,可用来实现数组扁平化
({}).valueOf() // {}
({}).toString() // "[object Object]"
([1,{}]).toString() // "1,[object Object]"
扩展
利用对象的 valueOf
和 toString
方法,我们可以实现很多效果
实现 a == 1 && a == 2 && a == 3 为真
javascript
var a = {
value: 1,
valueOf() {
return this.value++
}
// 或者
toString() {
return this.value++
}
}
运算规则
算术运算
运算符:
+
-
*
/
%
++
--
步骤:
- 将参与运算的变量转为
原始类型
数据 - 再将
原始类型
转为数字
后,进行运算
特殊:
- 在进行
+
运算的时候,出现字符串
,则进行字符串拼接 NaN
和任何类型数据进行运算,其结果都是NaN
,需要注意+
运算
javascript
console.log(9 + "6") // "96"
console.log(1 + undefined) // NaN
比较运算
1. 运算符:
>
<
>=
<=
步骤:
同算术运算
特殊:
- 两端全是字符串,比较字典顺序
- 两端存在
NaN
,一定为false
javascript
console.log(9 > "66") // false
console.log("9" > "66") // true
console.log(9 > NaN) // false
console.log(NaN > 9) // false
console.log(null > undefined) // false,相当于 0 > NaN
2. 运算符:
===
步骤:
只有类型和值都必须相同才为 true
特殊:
- 对象比较的是内存地址
- 两端存在
NaN
,一定为false
javascript
console.log(NaN === NaN) // false
console.log(NaN === undefined) // false
console.log(NaN === null) // false
3. 运算符
==
步骤:
- 两端类型相同,比较值
- 两端都是
原始类型
,转换成数字
后进行比较 - 一端是
原始类型
,一端是对象类型
,把原始类型
转换成原始类型
后进行比较
特殊:
undefined
和null
只有与自身比较或相互比较时,才会为false
- 两端存在
NaN
,一定为false
javascript
console.log(9 == "9") // true
console.log("" == []) // true
console.log(null == undefined) // true
console.log(NaN == NaN) // false
console.log(NaN == null) // false
console.log(null == 0) // false,null不会转为0
4. 运算符
!=
==
对 ==
和 ===
的结果进行取反
逻辑运算
!
&&
||
?:
!
是对数据转为 boolean
值后进行取反
&&
是返回第一个为 false
或最后一个为 true
的值
||
是返回第一个为 true
或最后一个为 false
的值
?:
属于三元运算符,为 true
返回第一个,为 false
返回第二个
javascript
console.log(!0) // true
console.log(!1) // false
console.log(1 && 2) // 2
console.log(null && 2) // null
console.log(1 && 2 && 3) // 3 为真,则返回最后一个为真的值
console.log(1 && null && undefined) // null 为假,则返回第一个为假的值
console.log(1 || 2) // 1
console.log(null || 2) // 2
console.log(null || 2 || 3) // 2 为真,则返回第一个为真的值
console.log(null || false || undefined) // undefined 为假,则返回最后一个为假的值
回顾
通过上述知识点的总结和了解,我们先来几道简单的题热热身
javascript
// 之前赘述过
console.log([].toString()) // ""
console.log([0].toString()) // "0"
console.log([] + []) // ""
/* 算数运算
1. 对象类型先转为原始类型 [] => ""
2. "" + "" => ""
*/
console.log(+[]) // 0
/* 算数运算
1. 对象类型先转为原始类型 [] => ""
2. +"" => 0
*/
// 对象取值
console.log(({a: "A"})["a"]) // "A"
console.log([1][0]) // 1
// 对象取值优先级高于运算符
var a = { value: 1 }
console.log(++a.value) // 2
console.log(++[1][0]) // 2
简单热身之后,我们来回顾下这道面试题
javascript
++[[]][+[]]+[+[]];
- 先看下
[[]][+[]]
这部分,为什么要先从这部分入手,是因为这部分是一个对象取值的过程,我们都知道,对象取值的优先级是高于运算符的 - 取值的话,那么我们就需要先看下属性部分
+[]
,即0
,那么第一步就变为[[]][0]
, 即[]
- 通过前两步,就变成了
++[]+[+[]]
的过程 - 我们来看下
++[]
,是一个算数运算过程,需要将[]
对象转为数字,先转为原始类型""
,再转为数字0
,即++0
,结果为1
- 经过第四步,就变成了
1+[+[]]
的过程,还是一个运算过程 - 我们把关注点放到
[+[]]
上,这个就相当于[0]
(+[]
我们上述热身分析过了) - 经过第六步,我们来到了最后的运算过程
1+[0]
,还是一个运算过程 - 我们还是需要将
[0]
转为原始类型"0"
,这块儿需要注意了,它的结果是一个字符串,而不是数字 - 我们来到了最后的战场
1+"0"
+
两端都是原始类型,且存在字符串,那么最终就是字符串拼接了,结果为"10"
,不是1 哦
恍然大悟,一道看似简单的面试题,内部蕴含了太多的基础知识点了,希望你能有收获。
一步一步夯实自己的基础,才能走的更远!