js中的基本类型
基本类型:number(只可以表示2的53次方-1),string,boolean,undefined,null,symbol,bigint(基于数组方式表示)
引用类型:Object, Array, Function, Date, RegExp, Map/Set/WeakMap/WeakSet (ES6+)
判断js的类型有以下几种方法
一、typeof判断法(基本类型判断)
通过上面的输出我们可以发现:typeof可以判断除了null以外的其他类型基本数据,
typeof判断引用数据类型,除了function 以外,其他的都返回object。
实现原理:typeof是通过将值的转化为二进制数来判断类型的,二进制的前三位为0那么就识别为对象,null的二进制全是0故判断为对象类型。
二、Instanceof(引用类型判断)
在理解 instanceof 之前,必须明确两个关键概念:
1.显式原型 (prototype):
- 是构造函数特有的属性
- 指向该构造函数创建的实例对象的原型对象
- 例如:Array.prototype 是所有数组实例的原型
2.隐式原型 (proto):
- 是每个对象都有的属性
- 指向创建该对象的构造函数的 prototype
- 现代代码中建议使用 Object.getPrototypeOf() 替代 proto
Instanceof方法本质上是判断左边的隐式原型是不是右边的显示原型。如果不是则继续判断左边隐式原型的隐式原型,一直判断下去,直到两边相等或者左边的值已经为null了。嘿嘿嘿,都知道原理了手搓一个代码不过分吧。
手搓Instanceof方法
function myInstanceof(left,right){
if(left === null){
return false;
}
let cur = left.__proto__;
while(cur){
if(cur === right.prototype){
return true;
}
cur = cur.__proto__;
}
return false;
}
三、Object.prototype.toString.call(任何类型)
Object.prototype.toString.call() 是 JavaScript 中用于检测对象类型的核心方法。它可以精确返回任意值的内部 [[Class]] 属性(格式为 [object Type])。
a.call(b) 的核心作用是:让函数 a 在执行时,其内部的 this 指向对象 b,也就是让b去临时借用a的方法。
Object.prototype.toString.call([])本质上是利用了 call 方法对 this 指向的控制能力。虽然 toString 是定义在 Object.prototype 上的方法,而 [] 是一个数组,但 JavaScript 通过 call,在不修改数组的前提下,临时让 Object.prototype.toString 方法在执行时,其内部的 this 指向这个数组 [] 。引擎在执行时会检查 this 的内部属性(如 [[Class]]),从而识别出它是一个数组,并返回标准字符串 "[object Array]"。整个过程执行结束后一切恢复原状,没有产生任何副作用。这种方式既安全又高效,是 JavaScript 中实现精确类型判断的核心机制。
与 typeof 和 instanceof 的对比
| 方法 | 特点 |
|---|---|
typeof | 无法区分对象类型(如 Array、Date 均返回 "object") |
instanceof | 受原型链影响,跨窗口/iframe 时会失效 |
Object.prototype.toString | 最可靠,能精确识别内置类型(包括 Null 和 Undefined) |
小小判断题
在进一步了解我们的判断之前我们先区分一下==与====
1.==会发送隐式类型转化,所以只判断值是不是相等的
2.===不会发生类型转化,所以只会判断值和类型是不是相等的
这是因为==发生的隐式转化,所以只判断值是否相等,故第一个输出的结果为true,但是第二个===不发生类型转化,数字类型与string类型显然是不相等的嘛。
隐式转化与显式转化
js引擎在执行各种运算符对数据是有要求的,比如if需要布尔类型的数据,如果数据不符合就会发生类型转化。
通俗的来说隐式转化也就是我们看不到的类型转化,由js引擎自动完成,开发者不直接转换代码。 显示转化也就是写出来的转化,是开发者主动调用的类型转化。引用类型可以转化为原始类型 这个是基于目的来的,因为我们的判断主要是四则运算这些,四则运算又是判断基本的数据类型。 但是原始类型是不可以转化为引用类型的
一、原始类型转原始类型
| 源 \ 目标 | string | number | boolean | bigint |
|---|---|---|---|---|
| string | — | "123" → 123 | "" → false | "123" → 123n |
| number | 123 → "123" | — | 0 → false | 123 → 123n |
| boolean | true → "true" | true → 1 | — | true → 1n |
| undefined | undefined → "undefined" | → NaN | → false | 抛 TypeError |
| null | null → "null" | → 0 | → false | 抛 TypeError |
| symbol | → "Symbol(x)" | 抛 TypeError | → true | 抛 TypeError |
valueOf()
默认实现里,包装对象(Number、String、Boolean、Date、BigInt 等) 会返回对应的基础类型值,普通对象({}、[])通常原样返回对象本身;
toString()
js中的大部分构造函数原型上面都重写了toString方法
- {}.toString返回由字符串
[object Object] - [].toString 返回由数组中的每个元素以逗号拼接而成的字符串
- xxx.toString 直接返回xxx的字符串字面量
二、引用类型转原始类型
1. 转布尔----任何引用类型转布尔都是true
2.转字符串--Stirng(obj)==>ToString(obj)==>Toprimitive(obj,string)
-
先看 obj 本身是不是“原始值”
如果是(number、string、boolean等),直接把它变成字符串返回,后面步骤全部跳过。 -
如果不是原始值(对象或数组等),先尝试调用
obj.toString()- 只要
toString()返回的是原始值(如"[object Object]"、"1,2,3"),就立刻用它。 - 如果
toString()返回的还是对象,继续下一步。
- 只要
-
再尝试调用
obj.valueOf()- 如果
valueOf()这回返回了原始值,就把它变成字符串返回。 - 如果还是对象,继续下一步。
- 如果
-
两步都失败 → 直接抛 TypeError
表示“我尽力了,实在不知道怎么把这家伙变成字符串”。
3.转数字——Number(obj)==>ToNumber(obj)==>Toprimitive(obj,Number)
Toprimitive(obj,Number)
- 判断obj是否为原始数据类型,若是则直接返回
- 否则,调用valueOf(),如果的到的是原始类型则返回
- 否则,调用toString(),如果得到的是原始类型则返回
- 否则,抛出TypeError的错误
经典面试题
console.log(1==[]);
类型不同触发隐式转换
比较 number == array,需要将数组转为原始值
执行 ToPrimitive([], 'number')
检查 Symbol.toPrimitive:数组没有定义 → 跳过
调用 valueOf():[].valueOf() 返回数组本身(非原始值)→ 继续
调用 toString():[].toString() 返回空字符串 ""
得到 Number("")
空字符串转为数字是 0
最终比较 1 == 0
结果为 false
console.log([]==![]);
![] 取反运算
[] 在布尔上下文中转为 true(所有对象转布尔都是 true)
!true 结果为 false
现在表达式变为:[] == false
false 转数字
根据规则:boolean 在 == 比较时会先转 number
Number(false) → 0
现在表达式变为:[] == 0
[] 转原始值
调用 [].valueOf() → 返回 [](仍是对象,继续转换)
调用 [].toString() → 返回 ""(空字符串)
现在表达式变为:"" == 0
"" 转数字
Number("") → 0
最终比较:0 == 0 → true