面试题答案
一键面试性能问题
- 方法调用开销:反射 API 方法(如
Reflect.get()
、Reflect.set()
等)是函数调用,相比直接属性访问,函数调用存在额外的开销,包括创建调用栈、参数传递等操作。例如:
const obj = { a: 1 };
// 直接访问属性
const value1 = obj.a;
// 使用反射 API
const value2 = Reflect.get(obj, 'a');
这里 Reflect.get
的函数调用开销比直接的 obj.a
要大。
- 动态解析开销:反射 API 是动态操作对象属性,每次操作都需要在运行时进行属性查找和解析,而静态属性访问在编译阶段就可以进行一些优化。比如在循环中频繁使用反射 API:
const obj = { a: 1, b: 2, c: 3 };
for (let prop of ['a', 'b', 'c']) {
// 每次循环都动态解析属性
const value = Reflect.get(obj, prop);
}
这相比静态访问,每次都要花费时间查找属性。
- 缓存缺失:由于反射 API 操作的动态性,难以利用缓存机制。例如,使用
Object.defineProperty
预先定义好属性,对象访问时可以利用属性查找的缓存,但反射 API 每次操作都类似新的查找,不能很好利用这种缓存。
优化方法
- 减少反射调用频率:能使用直接属性访问的地方尽量使用直接访问。只有在真正需要动态操作属性的场景下才使用反射 API。例如:
function accessProperty(obj, prop) {
if (typeof prop ==='string' && prop in obj) {
// 直接属性访问
return obj[prop];
}
// 只有在必要时使用反射
return Reflect.get(obj, prop);
}
- 缓存反射结果:对于重复操作的属性,可以缓存反射操作的结果。比如:
const obj = { a: 1, b: 2, c: 3 };
const cache = {};
function getPropertyCached(obj, prop) {
if (!cache[prop]) {
cache[prop] = Reflect.get(obj, prop);
}
return cache[prop];
}
for (let prop of ['a', 'b', 'c']) {
const value = getPropertyCached(obj, prop);
}
- 批量操作:将多次反射操作合并为一次。例如,要获取多个属性,可以使用
Object.fromEntries
和Reflect.ownKeys
结合:
const obj = { a: 1, b: 2, c: 3 };
const keys = Reflect.ownKeys(obj);
const result = Object.fromEntries(keys.map(key => [key, Reflect.get(obj, key)]));
这样相比多次单独的 Reflect.get
操作,减少了函数调用次数,提高性能。