前端面试题-类型判断
1. typeof
与 instanceof
类型判断
查看
typeof
是否能正确判断类型?instanceof
能否正确判断对象的原理是什么?
typeof
对于原始类型来说,除了 null
都可以显示正确的类型。判断 null
对应的结果为 object
。
1 | console.log(typeof 1); // number |

typeof
对于对象来说,除了函数都会显示 object
,所以说 typeof
不能准确判断对象的类型。
1 | console.log(typeof []); // object |

所以 typeof
不能正确判断类型。
如果我们想判断一个对象的正确类型,我们可以考虑使用
instanceof
,因为内部机制是通过原型链来判断的。但对于原始数据类型,使用instanceof
是无法判断的。
1 | function Person(){} |

注意:
instanceof
可以判断对象的正确类型,但由于原型链的关系,对于其类型的父类进行判断也是返回 true
。
手动实现 function isInstanceOf(child,Parent)
1 | function isInstanceof(Child, Parent) { |
1 | // 开始测试 |
2. ==
、===
、Object.is()
的区别
==
查看
相等(==)运算符(宽松比较)检查其两个操作数是否相等,返回一个布尔值结果。当比较双方数据类型不同时,它会尝试转换不同类型的操作数,并进行比较==
比较规则
-
如果操作数具有相同的类型,则按如下方式进行比较:
- 对象(Object):仅当两个操作数引用同一个对象时返回 true。
- 字符串(String):仅当两个操作数具有相同的字符且顺序相同时返回 true。
- 数字(Number):如果两个操作数的值相同,则返回 true。+0 和 -0 被视为相同的值。如果任何一个操作数是 NaN,返回 false;所以,NaN 永远不等于 NaN。
- 布尔值(Boolean):仅当操作数都为 true 或都为 false 时返回 true。
- 大整型(BigInt):仅当两个操作数的值相同时返回 true。
- 符号(Symbol):仅当两个操作数引用相同的符号时返回 true。
1
2
3let a = NaN;
let b = NaN;
console.log(a == b); // false -
如果其中一个操作数为 null 或 undefined,另一个操作数也必须为 null 或 undefined 以返回 true。否则返回 false。(null 和 undefined 除了与null 和 undefined 比较为 true,其他值都返回 false)
1
2
3
4
5
6
7
8let a = null;
let b = undefined;
let c = null;
let d = undefined;
console.log(a == b); // true
console.log(a == c); // true
console.log(b == d); // true
console.log(a == d); // true -
如果其中一个操作数是对象,另一个是原始值,则将对象转换为原始值。
- 在这一步,两个操作数都被转换为原始值(字符串、数字、布尔值、符号和大整型中的一个)。剩余的转换将分情况完成。
- 如果是相同的类型,使用步骤 1 进行比较。
- 如果其中一个操作数是符号(Symbol)而另一个不是,返回 false。
- 如果其中一个操作数是布尔值而另一个不是,则将布尔值转换为数字:true 转换为 1,false 转换为 0。然后再次对两个操作数进行宽松比较。
- 数字与字符串:将字符串转换为数字。转换失败将导致 NaN,这将保证相等比较为 false。
- 数字与大整型:按数值进行比较。如果数字的值为 ±∞ 或 NaN,返回 false。
- 字符串与大整型:使用与 BigInt() 构造函数相同的算法将字符串转换为大整型数。如果转换失败,返回 false。
- 在这一步,两个操作数都被转换为原始值(字符串、数字、布尔值、符号和大整型中的一个)。剩余的转换将分情况完成。
对象转为原始值
对象将依次调用它的 [Symbol.toPrimitive]()
(将 default 作为 hint 值)、valueOf() 和 toString() 方法,将其转换为原始值。注意,原始值转换会在 toString() 方法之前调用 valueOf() 方法。
[Symbol.toPrimitive]()
方法,如果存在,则必须返回原始值,返回对象,会导致 TypeError。对于 valueOf() 和 toString(),如果其中一个返回对象,则忽略其返回值,从而使用另一个的返回值;如果两者都不存在,或者两者都没有返回一个原始值,则抛出 TypeError。
示例
定义一个干净的对象
1 | let a = {} |
实现该对象的[Symbol.toPrimitive]
方法,使其返回一个原始值。
1 | let a = { |
注意
当未对对象使用强制类型转换时,会返回默认值。但Symbol.toPrimitive
返回的 null 或 undefined 与 null 和 undefined 进行宽松比较(==)时仍为 false,hint 值是 number 时,返回的必须是数字字符串或数字,否则与数字或数字字符串进行宽松比较时仍为 false
Date 和 Symbol 对象是唯一重写 [Symbol.toPrimitive]()
方法的对象。
对于 Date,如果 hint 是 “string” 或 “default”,[Symbol.toPrimitive]()
将会调用 toString。如果 toString 属性不存在,则调用 valueOf。如果 valueOf 也不存在,则抛出一个TypeError。
对于 Symbol,hint 参数未使用时,Symbol 的 [Symbol.toPrimitive]()
方法返回 Symbol 对象的原始值作为数据类型返回。
1 | const sym = Symbol("example"); |
当对象不存在Symbol.toPrimitive
时,会根据 toString()
和 valueOf()
的返回值作为原始值进行比较。
1 | let a = { |
一般来说,valueOf 的优先级高于 toString。但当其使用强制字符串类型转换时,会根据 toString 的返回值作为原始值进行比较。
===
查看
严格相等(===)运算符与 == 运算符之间最显著的区别是,严格相等运算符不尝试类型转换。相反,严格相等运算符总是认为不同类型的操作数是不同的,即只要类型不同进行 `===` 操作时,结果即为 `false`。严格相等运算符本质上只执行第 1 步,然后对所有其他情况返回 `false`。上面的算法有一个“故意违反”:如果其中一个操作数是 document.all,则它被视为 undefined。这意味着 document.all == null 是 true,但 document.all === undefined && document.all === null 是 false。 —— MDN文章描述
===
比较规则
-
如果操作数的类型不同,则返回 false。
-
如果两个操作数都是对象,只有当它们指向同一个对象时才返回 true。
-
如果两个操作数都为 null,或者两个操作数都为 undefined,返回 true。
-
如果两个操作数有任意一个为 NaN,返回 false。
-
否则,比较两个操作数的值:
- 数字类型必须拥有相同的数值。+0 和 -0 会被认为是相同的值。
- 字符串类型必须拥有相同顺序的相同字符。
- 布尔运算符必须同时为 true 或同时为 false。
1 | console.log(-0 === +0); // true |
Object.is()
查看
Object.is()
静态方法确定两个值是否为相同值。
Object.is()
的比较规则
如果出现以下几种情况,则返回 true,即比较的两个值相同
-
都是 undefined
-
都是 null
-
都是 true 或者都是 false
-
都是长度相同、字符相同、顺序相同的字符串
-
都是相同的对象(意味着两个值都引用了内存中的同一对象)
-
都是 BigInt 且具有相同的数值
-
都是 symbol 且引用相同的 symbol 值
-
都是数字且
- 都是 +0
- 都是 -0
- 都是 NaN
- 都有相同的值,非零且都不是 NaN
Object.is()
与 == 运算符并不等价。== 运算符在测试相等性之前,会对两个操作数进行类型转换(如果它们不是相同的类型),这可能会导致一些非预期的行为,例如 “” == false 的结果是 true,但是 Object.is()
不会对其操作数进行类型转换。
Object.is()
也不等价于 === 运算符。Object.is()
和 === 之间的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符(和 == 运算符)将数值 -0 和 +0 视为相等,但是会将 NaN 视为彼此不相等。
1 | Object.is(0, -0); // false |
总结
对于 ==
,主要特点就是类型转换,而 ===
和 Object.is()
则不进行类型转换。
对于 ===
,主要特点就是严格相等,类型不同就是 false
,与 ==
类型相同时比较规则一致。
对于 Object.is()
,主要特点就是严格相等,类型不同就是 false
,与 ==
类型相同时比较规则一致。但与 ===
不同的是,Object.is()
对于 0 和 -0、NaN 的处理不同,Object.is()
将数值 -0 和 +0 视为彼此不相等,但是会将 NaN 视为彼此相等,===
将数值 -0 和 +0 视为相等,但是会将 NaN 视为彼此不相等。
全局 isNaN()
和 Number.isNaN()
查看
-
isNaN()
函数用来确定一个值是否为 NaN,若有必要,则首先将值转换为数字。 -
Number.isNaN()
静态方法判断传入的值是否为 NaN,如果输入不是数字类型,则返回 false。它是全局isNaN()
函数更健壮的版本。
isNaN()
是全局对象的一个函数属性。对于数字值,isNaN()
检测该值是否为 NaN 值。当 isNaN()
函数的参数不是数字类型时,其会首先被转换为数字,然后将其结果值与 NaN 进行比较
isNaN()
对于非数字参数的行为可能会令人困惑!例如,空字符串被强制转换为 0,布尔值被强制转换为 0 或 1;直观上,两者均“不是数字”,仅因它们的运算结果不是 NaN,而使得 isNaN()
返回 false。因此,isNaN()
既不回答“输入是否为浮点数值 NaN”,也不回答“输入是否为数字”这两个问题。
Number.isNaN() 是检测一个值是否为数字值 NaN 的更可靠的方法。或者,也可以使用表达式 x !== x,这两种方法都不会产生全局 isNaN() 不可靠的误判。要检测一个值是否为数字,请使用 typeof x === “number”。
Number.isNaN() 和全局 isNaN() 之间的区别
Number.isNaN() 不会尝试将参数转换为数字,因此非数字总是返回 false。以下都返回 false:
1 | Number.isNaN("NaN"); |
全局 isNaN() 函数会将参数强制转换为数字:
1 | isNaN("NaN"); // true |