🔥 JavaScript 类型判断大揭秘:从 typeof 到 instanceof 的 "坑" 与 "糖"

78 阅读4分钟

今天咱们来聊聊 JavaScript 里最让人头秃的话题之一 —— 类型判断。就像生活中我们经常认错人,JS 里的类型判断也常常给我们开各种玩笑。

一、JavaScript 的 "物种分类"

首先得明确,JS 世界里的 "生物" 主要分两大类:

  1. 原始类型:number(数字)、string(字符串)、boolean(布尔)、undefined(未定义)、null(空值)、Symbol(符号)、Bigint(大整数)

    这些就像自然界的基本粒子,简单纯粹,不可再分。比如123'hello'true都是原始类型,就像猫科动物里的狮子老虎,虽然都是猫科但各有特点。

  2. 引用类型: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"

它的槽点:

  1. null的判断堪称史诗级 bug:
typeof null; // "object" (黑人问号脸?)

这是因为 JS 诞生时的一个设计失误:null的二进制表示全是 0,而所有对象的二进制前三位都是 0,于是typeof就把null错认为对象了。就像把熊猫当成了猫,虽然都带个 "猫" 字,但人家可是国宝级的特殊存在!

  1. 对引用类型的判断基本靠猜:
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 (函数:又被认出来了)

它的短板:

  1. 原始类型无能为力
123 instanceof Number; // false

'hello' instanceof String; // false

就像查族谱查不到石头的祖宗,原始类型根本没有原型链

  1. 原型链被修改时会失效:
// 人为修改原型链

function Parent() {}

function Child() {}

Child.prototype = new Parent();

const child = new Child();

child instanceof Child; // false (认不出自己了)

这就像有人改了家谱,结果亲儿子都认不出来了。

四、Object.prototype.toString.call (x):类型判断的 "照妖镜"

这是 JS 里最靠谱的类型判断方法,堪称照妖镜。不管你是什么妖魔鬼怪,它都能现出原形。

工作原理:

  1. 如果this的值是undefined,返回[object Undefined]

  2. 如果this的值是null,返回[object Null]

  3. 把要判断的值转换成对象(ToObject

  4. 获取这个对象的内部属性[[class]]

  5. 返回字符串'[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"

这个方法的优点是:

  • 能准确识别所有类型,包括nullundefined

  • 不受原型链修改的影响

  • 兼容性好,从 ES3 到 ES6 都支持

就像 DNA 检测,不管你穿什么衣服、整什么容,一查就知道你到底是谁。

五、Array.isArray (x):数组的 "专属身份证"

虽然Object.prototype.toString.call()已经能判断数组了,但 JS 还是专门给数组搞了个Array.isArray()方法,可见数组的地位不一般。

Array.isArray([]); // true

Array.isArray({}); // false

Array.isArray('hello'); // false

这个方法的好处是更简洁直观,一看就知道是在判断是不是数组。就像专门给熊猫办了个身份证,不用再去查 "猫科动物图鉴" 了。

总结:类型判断工具选择指南

  1. 判断原始类型:用typeof(记得单独处理null

  2. 判断引用类型是否属于某个构造函数的实例:用instanceof

  3. 需要精准判断所有类型:用Object.prototype.toString.call()

  4. 专门判断数组:用Array.isArray()

记住这些,下次在 JS 类型判断的迷宫里就不会迷路了。最后送大家一句话:在 JS 的世界里,永远不要相信表面现象,多做几次 "体检" 才能确定类型哦!