什么是 JavaScript 中的反射与内省,为什么你需要了解它们?
\n\n想象一下,你是一名程序员侦探。你有一个对象,但不知道它内部有什么。它有哪些属性?它本质是什么——数组、函数,还是一组数据?在日常生活中,我们只需查看代码,但在 JavaScript 的动态世界中,你的程序必须能够自行做到这一点。这就是反射与内省。
\n\n这些术语听起来像是哲学,但实际上它们是强大的工具。内省是指代码审视自身:检查变量的类型、获取函数名称或枚举对象的属性。反射则更进一步——它是代码在运行时修改自身的能力:添加新方法、更改原型,甚至创建代理对象。
\n\n乍一看,这似乎只有复杂框架的作者才需要。但实际上,你每天都在使用内省,甚至没有注意到。当你编写 typeof x === 'string' 时——你已经在进行内省了。而当你使用 Object.assign 复制属性时——你就在应用反射。
在本文中,我们将介绍所有关键方法和技巧。你将学习如何“审视”任何对象的内部,如何安全地处理未知数据,以及如何编写更灵活、更具适应性的代码。开始吧!
\n\n内省基础:看看我们手里有什么
\n\n内省从一个简单的问题开始:“这是什么?”在 JavaScript 中,有几个内置的运算符和方法可以帮助回答这个问题。
\n\ntypeof 运算符:快速类型检查
\n\n这是你的第一个帮手。typeof 返回一个字符串,指示操作数的类型。它很简单,但有一些陷阱。
console.log(typeof 42); // "number"\nconsole.log(typeof 'hello'); // "string"\nconsole.log(typeof true); // "boolean"\nconsole.log(typeof undefined); // "undefined"\nconsole.log(typeof null); // "object" — 这就是那个著名的 bug!\nconsole.log(typeof []); // "object" — 这不再是 bug,而是特性\nconsole.log(typeof function(){}); // "function"\n\n请注意:typeof null 返回 "object"。这是 JavaScript 的一个历史性错误,不会再修复。typeof [] 也返回 "object",这很合理,因为数组是对象的一种子类型。但如何区分数组和普通对象呢?这需要其他工具。
一个实用技巧:typeof 非常适合检查变量是否已定义,而不会冒抛出 ReferenceError 异常的风险。
if (typeof someGlobalVar !== 'undefined') {\n // 安全地使用 someGlobalVar\n}\n\ninstanceof:检查原型链
\n\n如果说 typeof 回答的是“是什么原始类型?”,那么 instanceof 回答的是“是否是某个类的实例?”。它检查构造函数的原型是否存在于对象的原型链中。
const arr = [1, 2, 3];\nconsole.log(arr instanceof Array); // true\nconsole.log(arr instanceof Object); // true — 因为 Array 继承自 Object\n\nconst date = new Date();\nconsole.log(date instanceof Date); // true\nconsole.log(date instanceof Object); // true\n\n// 但对于原始类型不起作用\nconsole.log('hello' instanceof String); // false — 原始类型不是对象\n\n一个重要细节:instanceof 在处理 iframe 或不同执行上下文(例如 Node.js)时可能会失效。每个上下文都有自己的全局对象,如果数组是在另一个框架中创建的,[] instanceof window.Array 可能返回 false。对于这种情况,最好使用 Array.isArray()。