反射是编程语言的一种能力,允许程序在运行时获取、操作自身的信息,比如查看对象的属性、调用方法、修改对象结构等。JavaScript在ES6之前没有原生的反射机制,开发者通常通过Object.getOwnPropertyDescriptor、Object.keys等零散的API实现部分反射能力,ES6正式引入了Reflect对象,将反射相关的操作进行了统一封装,成为JavaScript原生反射的核心实现。

JavaScript反射的实现基础
JavaScript的反射能力建立在语言本身的动态特性之上,对象的结构、属性、方法都可以在运行时被访问和修改。ES6提供的Reflect对象是一个内置对象,它提供了一系列静态方法,这些方法与Proxy陷阱的方法一一对应,专门用于拦截和操作对象的底层行为,这就是JavaScript实现反射的核心方式。
Reflect的所有方法都是静态的,不需要通过new关键字实例化,直接调用Reflect.xxx()即可使用。它的方法设计目的是将之前分散在Object、Function等对象上的反射相关操作进行规范化,同时让反射操作的结果更符合预期,比如之前delete obj.prop返回的是布尔值表示是否删除成功,而Reflect.deleteProperty(obj, prop)同样返回布尔值,语义更清晰。
Reflect核心API详解
Reflect对象提供了13个静态方法,覆盖了对象操作的大部分反射场景,下面逐一介绍常用API的使用方式。
1. 属性操作相关API
Reflect.get(target, propertyKey, receiver)
该方法用于读取对象的属性值,等价于target[propertyKey],但提供了更统一的反射入口,第三个参数receiver可以指定读取属性时的this指向。
// 基础使用
const obj = {
name: 'test',
get info() {
return this.name + '_info';
}
};
// 读取普通属性
const name = Reflect.get(obj, 'name');
console.log(name); // 输出 test
// 读取访问器属性,指定receiver改变this指向
const receiverObj = { name: 'receiver_test' };
const info = Reflect.get(obj, 'info', receiverObj);
console.log(info); // 输出 receiver_test_info
Reflect.set(target, propertyKey, value, receiver)
该方法用于设置对象的属性值,等价于target[propertyKey] = value,返回布尔值表示设置是否成功。
const obj = {
set age(val) {
if (val >= 0) {
this._age = val;
}
}
};
// 设置属性,指定receiver改变this指向
const receiverObj = {};
const result = Reflect.set(obj, 'age', 18, receiverObj);
console.log(result); // 输出 true
console.log(receiverObj._age); // 输出 18
console.log(obj._age); // 输出 undefined
Reflect.has(target, propertyKey)
该方法用于判断对象是否包含某个属性,等价于propertyKey in target,返回布尔值。
const obj = { a: 1, b: 2 };
const hasA = Reflect.has(obj, 'a');
console.log(hasA); // 输出 true
const hasC = Reflect.has(obj, 'c');
console.log(hasC); // 输出 false
Reflect.deleteProperty(target, propertyKey)
该方法用于删除对象的属性,等价于delete target[propertyKey],返回布尔值表示删除是否成功。
const obj = { a: 1, b: 2 };
const delResult = Reflect.deleteProperty(obj, 'a');
console.log(delResult); // 输出 true
console.log(obj); // 输出 { b: 2 }
Reflect.ownKeys(target)
该方法用于返回对象自身的所有属性键,包括字符串键和Symbol键,等价于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。
const sym = Symbol('test');
const obj = { a: 1, [sym]: 2 };
const keys = Reflect.ownKeys(obj);
console.log(keys); // 输出 ['a', Symbol(test)]
2. 对象结构操作相关API
Reflect.defineProperty(target, propertyKey, attributes)
该方法用于定义对象的属性,等价于Object.defineProperty,但返回布尔值表示定义是否成功,而Object.defineProperty失败会抛出错误。
const obj = {};
const defineResult = Reflect.defineProperty(obj, 'name', {
value: 'test',
writable: false
});
console.log(defineResult); // 输出 true
console.log(obj.name); // 输出 test
// 尝试修改不可写属性,返回false而不是报错
const resetResult = Reflect.defineProperty(obj, 'name', {
value: 'new'
});
console.log(resetResult); // 输出 false
Reflect.getOwnPropertyDescriptor(target, propertyKey)
该方法用于获取对象自身属性的描述符,等价于Object.getOwnPropertyDescriptor。
const obj = { name: 'test' };
const descriptor = Reflect.getOwnPropertyDescriptor(obj, 'name');
console.log(descriptor); // 输出 { value: 'test', writable: true, enumerable: true, configurable: true }
Reflect.preventExtensions(target)
该方法用于阻止对象被扩展,等价于Object.preventExtensions,返回布尔值表示操作是否成功。
const obj = { a: 1 };
const preventResult = Reflect.preventExtensions(obj);
console.log(preventResult); // 输出 true
console.log(Object.isExtensible(obj)); // 输出 false
// 尝试添加属性,返回false
const addResult = Reflect.set(obj, 'b', 2);
console.log(addResult); // 输出 false
Reflect.isExtensible(target)
该方法用于判断对象是否可扩展,等价于Object.isExtensible。
const obj = {};
console.log(Reflect.isExtensible(obj)); // 输出 true
Reflect.preventExtensions(obj);
console.log(Reflect.isExtensible(obj)); // 输出 false
3. 函数与原型操作相关API
Reflect.apply(target, thisArgument, argumentsList)
该方法用于调用函数,等价于Function.prototype.apply.call(target, thisArgument, argumentsList),可以安全地调用函数,避免apply被修改的问题。
function sum(a, b) {
return a + b;
}
// 调用函数,指定this和参数
const result = Reflect.apply(sum, null, [1, 2]);
console.log(result); // 输出 3
// 调用数组的push方法
const arr = [1, 2];
Reflect.apply(Array.prototype.push, arr, [3, 4]);
console.log(arr); // 输出 [1, 2, 3, 4]
Reflect.construct(target, argumentsList, newTarget)
该方法用于调用构造函数创建实例,等价于new target(...argumentsList),第三个参数可以指定实例的原型。
function Person(name) {
this.name = name;
}
Person.prototype.say = function() {
return this.name;
};
// 创建实例
const p = Reflect.construct(Person, ['test']);
console.log(p.name); // 输出 test
console.log(p.say()); // 输出 test
// 指定原型
function Animal() {}
const p2 = Reflect.construct(Person, ['animal'], Animal);
console.log(p2 instanceof Animal); // 输出 true
console.log(p2 instanceof Person); // 输出 false
Reflect.getPrototypeOf(target)
该方法用于获取对象的原型,等价于Object.getPrototypeOf。
const obj = {};
const proto = Reflect.getPrototypeOf(obj);
console.log(proto === Object.prototype); // 输出 true
Reflect.setPrototypeOf(target, prototype)
该方法用于设置对象的原型,等价于Object.setPrototypeOf,返回布尔值表示设置是否成功。
const obj = {};
const result = Reflect.setPrototypeOf(obj, Array.prototype);
console.log(result); // 输出 true
console.log(obj instanceof Array); // 输出 true
反射与Proxy的配合使用
反射和Proxy是紧密相关的两个特性,Proxy用于拦截对象的操作,而Reflect用于在拦截器中执行默认的拦截操作,二者的陷阱方法和Reflect的API是一一对应的。
比如我们要拦截对象的属性读取操作,同时保留默认的读取逻辑,就可以在Proxy的get陷阱中调用Reflect.get:
const obj = { name: 'test', age: 18 };
const proxy = new Proxy(obj, {
get(target, prop, receiver) {
console.log(`读取属性 ${prop}`);
// 调用Reflect.get执行默认的读取逻辑
return Reflect.get(target, prop, receiver);
}
});
console.log(proxy.name);
// 输出:
// 读取属性 name
// test
这种配合方式让Proxy的拦截逻辑更清晰,也避免了直接操作target[prop]可能出现的this指向问题。
反射的使用场景
- 实现依赖注入:通过反射获取类的构造函数参数、属性信息,自动注入依赖。
- 数据校验:在设置对象属性时,通过反射拦截并校验属性值的合法性。
- 元数据处理:给对象添加额外的元数据,通过反射读取这些元数据进行逻辑处理。
- 框架开发:很多前端框架会用反射实现响应式、依赖追踪等底层能力。
注意事项
反射虽然强大,但也不要过度使用,因为反射操作会绕过JavaScript的常规语法检查,可能导致代码可读性下降。另外Reflect的方法都是操作对象底层的,需要注意避免修改内置对象的原型,防止影响全局的代码逻辑。
JavaScript反射ReflectProxy元编程修改时间:2026-06-11 09:48:46