今天咱们来聊聊 JavaScript 里最让人头秃的话题之一 —— 类型判断。就像生活中我们经常认错人,JS 里的类型判断也常常给我们开各种玩笑。
一、JavaScript 的 "物种分类"
首先得明确,JS 世界里的 "生物" 主要分两大类:
-
原始类型:number(数字)、string(字符串)、boolean(布尔)、undefined(未定义)、null(空值)、Symbol(符号)、Bigint(大整数)
这些就像自然界的基本粒子,简单纯粹,不可再分。比如
123、'hello'、true都是原始类型,就像猫科动物里的狮子老虎,虽然都是猫科但各有特点。 -
引用类型:Object(对象)、Array(数组)、Function(函数)、new Date()(日期)等等
这些家伙就复杂多了,像是由基本粒子组成的复杂生物。最坑的是,数组其实是对象的 "亚种",函数也是对象的 "远方亲戚"。
二、typeof:看似靠谱的 "初步体检"
typeof就像医院的初诊医生,能快速给你一个大概判断,但经常误诊。
它的优点:
- 能准确识别除了
null以外的所有原始类型:
typeof 123; // "number"
typeof 'hello'; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol(1); // "symbol"
typeof 123123123n; // "bigint"
它的槽点:
- 对
null的判断堪称史诗级 bug:
typeof null; // "object" (黑人问号脸?)
这是因为 JS 诞生时的一个设计失误:null的二进制表示全是 0,而所有对象的二进制前三位都是 0,于是typeof就把null错认为对象了。就像把熊猫当成了猫,虽然都带个 "猫" 字,但人家可是国宝级的特殊存在!
- 对引用类型的判断基本靠猜:
typeof []; // "object" (数组:我明明是数组啊!)
typeof {}; // "object" (对象:这个没毛病)
typeof new Date(); // "object" (日期:我很特殊的好吗)
typeof function() {}; // "function" (函数:终于有个认识我的了)
只有函数能被typeof准确识别,其他引用类型一律被贴上"object"的标签。就像把老虎、狮子、豹子都叫做 "猫",虽然没错但等于没说。
三、instanceof:家族溯源专家
如果说typeof是看表面,那instanceof就是查族谱的。它的工作原理是:
沿着对象的原型链往上找,看看能不能找到某个构造函数的原型。
它的特长:
- 准确判断引用类型的具体 "门派":
[] instanceof Array; // true (数组终于找到组织了)
new Date() instanceof Date; // true (日期表示很满意)
function() {} instanceof Function; // true (函数:又被认出来了)
它的短板:
- 对
原始类型无能为力:
123 instanceof Number; // false
'hello' instanceof String; // false
就像查族谱查不到石头的祖宗,原始类型根本没有原型链。
- 原型链被修改时会失效:
// 人为修改原型链
function Parent() {}
function Child() {}
Child.prototype = new Parent();
const child = new Child();
child instanceof Child; // false (认不出自己了)
这就像有人改了家谱,结果亲儿子都认不出来了。
四、Object.prototype.toString.call (x):类型判断的 "照妖镜"
这是 JS 里最靠谱的类型判断方法,堪称照妖镜。不管你是什么妖魔鬼怪,它都能现出原形。
工作原理:
-
如果this的值是undefined,返回
[object Undefined] -
如果this的值是null,返回
[object Null] -
把要判断的值转换成对象(
ToObject) -
获取这个对象的内部属性
[[class]] -
返回字符串
'[object 类型名]'
用法示例:
function getType(x) {
const typeStr = Object.prototype.toString.call(x);
return typeStr.slice(8, -1); // 截取中间的类型名
}
getType(123); // "Number"
getType('hello'); // "String"
getType(null); // "Null" (终于对了!)
getType(undefined); // "Undefined"
getType([]); // "Array"
getType({}); // "Object"
getType(new Date()); // "Date"
getType(function() {}); // "Function"
这个方法的优点是:
-
能准确识别所有类型,包括
null和undefined -
不受原型链修改的影响
-
兼容性好,从 ES3 到 ES6 都支持
就像 DNA 检测,不管你穿什么衣服、整什么容,一查就知道你到底是谁。
五、Array.isArray (x):数组的 "专属身份证"
虽然Object.prototype.toString.call()已经能判断数组了,但 JS 还是专门给数组搞了个Array.isArray()方法,可见数组的地位不一般。
Array.isArray([]); // true
Array.isArray({}); // false
Array.isArray('hello'); // false
这个方法的好处是更简洁直观,一看就知道是在判断是不是数组。就像专门给熊猫办了个身份证,不用再去查 "猫科动物图鉴" 了。
总结:类型判断工具选择指南
-
判断原始类型:用
typeof(记得单独处理null) -
判断引用类型是否属于某个构造函数的实例:用
instanceof -
需要精准判断所有类型:用
Object.prototype.toString.call() -
专门判断数组:用
Array.isArray()
记住这些,下次在 JS 类型判断的迷宫里就不会迷路了。最后送大家一句话:在 JS 的世界里,永远不要相信表面现象,多做几次 "体检" 才能确定类型哦!