JavaScript元编程指的是程序在运行时能够检查、修改甚至生成自身结构的能力,而反射API就是JavaScript提供的一组用于实现这种能力的原生接口,最核心的组成部分是Reflect对象和Proxy构造函数。通过这两个特性,我们可以突破常规编程的限制,对对象的操作行为进行自定义。

什么是反射API
反射API是ES6引入的一组内置对象和方法,主要载体是Reflect,它提供了一系列与对象操作对应的静态方法,这些方法原本分散在Object对象或者其他语法行为中,Reflect将它们统一收拢,并且所有方法的返回值都是明确的布尔值或者操作结果,不会出现像Object.defineProperty失败就抛出错误的情况。
常用的Reflect方法如下:
| 方法名 | 作用 | 对应常规操作 |
|---|---|---|
| Reflect.get(target, prop, receiver) | 获取对象属性值 | target[prop] |
| Reflect.set(target, prop, value, receiver) | 设置对象属性值 | target[prop] = value |
| Reflect.has(target, prop) | 判断对象是否有某个属性 | prop in target |
| Reflect.deleteProperty(target, prop) | 删除对象属性 | delete target[prop] |
| Reflect.apply(func, thisArg, args) | 调用函数 | func.apply(thisArg, args) |
下面是一个简单的Reflect使用示例:
// 定义基础对象
const user = {
name: '张三',
age: 20
};
// 使用Reflect获取属性
const userName = Reflect.get(user, 'name');
console.log(userName); // 输出:张三
// 使用Reflect设置属性
Reflect.set(user, 'age', 21);
console.log(user.age); // 输出:21
// 使用Reflect判断属性是否存在
const hasName = Reflect.has(user, 'name');
console.log(hasName); // 输出:true
// 使用Reflect删除属性
Reflect.deleteProperty(user, 'age');
console.log(user.age); // 输出:undefined
Proxy代理与元编程的结合
Proxy是ES6提供的代理器,它可以包装一个目标对象,拦截该对象的基本操作,比如属性读取、赋值、函数调用等,而这些拦截逻辑的实现,通常就需要配合Reflect方法来完成,两者结合才是JavaScript元编程的核心用法。
Proxy的构造函数接收两个参数,第一个是目标对象,第二个是处理器对象,处理器对象中可以定义各种拦截陷阱,下面是常见的陷阱类型:
- get陷阱:拦截对象属性读取操作
- set陷阱:拦截对象属性赋值操作
- has陷阱:拦截
in操作符 - apply陷阱:拦截函数调用操作
- construct陷阱:拦截
new操作符
属性访问拦截示例
我们可以通过Proxy实现对象属性的默认值功能,当访问不存在的属性时返回默认值,而不是undefined:
// 创建带默认值的对象代理
function createDefaultObject(target, defaultVal) {
return new Proxy(target, {
get(target, prop, receiver) {
// 如果属性存在,返回原属性值,否则返回默认值
if (Reflect.has(target, prop)) {
return Reflect.get(target, prop, receiver);
}
return defaultVal;
}
});
}
const config = createDefaultObject({ port: 8080 }, '未配置');
console.log(config.port); // 输出:8080
console.log(config.host); // 输出:未配置
函数调用劫持示例
通过apply陷阱可以拦截函数调用,在函数执行前后添加额外的逻辑,比如日志打印、参数校验等:
// 原函数
function sum(a, b) {
return a + b;
}
// 创建函数代理
const sumProxy = new Proxy(sum, {
apply(target, thisArg, args) {
console.log('函数开始执行,参数:', args);
// 调用原方法
const result = Reflect.apply(target, thisArg, args);
console.log('函数执行结束,结果:', result);
return result;
}
});
sumProxy(1, 2);
// 输出:
// 函数开始执行,参数: [1, 2]
// 函数执行结束,结果: 3
// 返回:3
实际应用场景
JavaScript元编程与反射API在实际开发中有很多实用场景:
- 数据校验:在对象属性赋值时通过set陷阱校验值的合法性,不符合要求则拒绝赋值
- 响应式系统:Vue3的响应式核心就是基于Proxy实现的,拦截属性访问和赋值来触发依赖更新
- 接口请求封装:拦截函数调用自动添加请求头、处理错误、统一返回格式
- 对象行为模拟:模拟只读对象、不可枚举对象等特殊行为的对象
下面是一个简单的数据校验示例,限制用户对象的age属性只能设置为数字且大于0:
const userProxy = new Proxy({ name: '', age: 0 }, {
set(target, prop, value, receiver) {
if (prop === 'age') {
if (typeof value !== 'number' || value <= 0) {
throw new Error('年龄必须是大于0的数字');
}
}
// 合法则设置属性
return Reflect.set(target, prop, value, receiver);
}
});
userProxy.name = '李四';
userProxy.age = 25; // 正常赋值
console.log(userProxy.age); // 输出:25
// userProxy.age = -5; // 抛出错误:年龄必须是大于0的数字
// userProxy.age = '二十'; // 抛出错误:年龄必须是大于0的数字
需要注意的是,Proxy只能代理对象本身的操作,无法代理对象原型链上的操作,而且Proxy是ES6的特性,在部分非常老旧的浏览器中不支持,使用时需要根据项目的兼容性要求做判断。
JavaScript元编程反射APIProxyReflect修改时间:2026-07-01 13:03:33