get拦截器是Proxy访问不存在属性的关键入口,需先用Reflect.ownKeys判断自身属性是否存在,再结合getOwnPropertyDescriptor排除getter/setter,最后对真非法访问抛错。

Proxy 代理对象时,get 拦截器是关键入口
访问不存在的属性触发的是 get 拦截器,不是 has 或 ownKeys。只要目标对象上没有该属性(包括原型链上也查不到),且你没在 get 里显式返回值,就会走默认行为(返回 undefined)。想抛错,就得在 get 里主动判断。
注意:不能只检查 target.hasOwnProperty(key),因为这会漏掉原型上的自有属性;更稳妥的是用 Object.prototype.hasOwnProperty.call(target, key) 配合 key in target 判断是否“真正不可访问”——但实际中,多数场景只需拦截“目标对象自身无、且非 Symbol/内部方法”的访问。
- 推荐先用
Reflect.get(target, key, receiver)获取原始值,再判断是否为undefined且该 key 不是函数名(避免误伤toString等) - 排除常见内置属性:
key === 'toString' || key === 'valueOf' || typeof key === 'symbol' - 如果目标对象是类实例,还要小心
constructor、__proto__等,建议白名单放行或跳过
抛错前要区分“真不存在”和“值就是 undefined”
直接对所有 undefined 返回值抛错会破坏正常逻辑——比如一个合法字段本就初始化为 undefined,或者被显式设为 undefined。必须结合属性是否存在做双重判断。
最简可靠方式是:先用 Object.keys(target).includes(key) 或 Reflect.ownKeys(target).includes(key) 查自身属性;若不包含,再确认它不是访问器属性(Object.getOwnPropertyDescriptor(target, key) 返回 undefined),才认定为“非法访问”。
安装并运行 Aperture(Lightning Labs 的 L402 闪电反向代理),用于创建 L402 付费墙、配置付费 API 端点、为其他代理托管付费内容或测试 L402 认证流程。
-
Reflect.ownKeys(target)比Object.keys()更全,能捕获不可枚举属性和 Symbol - 如果目标对象有 getter/setter,
getOwnPropertyDescriptor会返回描述符,此时不应报错 - 别忘了检查
key类型:字符串或 Symbol,避免对0、null等意外输入抛错
完整可复用的友好提示 Proxy 封装
下面是一个轻量封装,支持传入自定义提示模板,且避开常见陷阱:
function createStrictProxy(target, options = {}) {
const { message = 'Property "%s" does not exist on %s', className = target.constructor?.name || 'object' } = options;
return new Proxy(target, {
get(obj, key, receiver) {
// 放行原型方法、Symbol、私有字段(_ 开头可选)、内部属性
if (typeof key === 'symbol' || key in Object.prototype || key === 'constructor') return Reflect.get(obj, key, receiver);
const desc = Object.getOwnPropertyDescriptor(obj, key);
if (desc !== undefined) return Reflect.get(obj, key, receiver); // 有描述符,说明是合法定义(含 getter)
// 真正不存在:自身无 + 非原型继承来的方法
const ownKeys = Reflect.ownKeys(obj);
if (!ownKeys.includes(key)) {
throw new ReferenceError(
message.replace('%s', `"${String(key)}"`).replace('%s', className)
);
}
return Reflect.get(obj, key, receiver);
}
});
}
使用示例:
const user = createStrictProxy({ name: 'Alice' }, { message: '⚠️ No such field: %s on %s' });
user.name; // 'Alice'
user.age; // 抛出 ReferenceError: ⚠️ No such field: "age" on object
容易被忽略的边界情况
Proxy 的 get 拦截器不会捕获数组索引访问的“越界”,比如 arr[100] 即使 length === 0 也只返回 undefined,不会进 get——因为数组的索引访问走的是 [[Get]] 内部方法,而 Proxy 对稀疏数组索引的拦截依赖于 target 是否为 Array 且是否设置了 length 等元信息。简单起见,如需拦截数组越界,应单独处理或改用类包装。
- Proxy 无法拦截
for...in中的原型属性遍历,get不触发;要用ownKeys+getOwnPropertyDescriptor组合控制 - 严格模式下,对 non-configurable 属性赋
undefined会静默失败,但 Proxy 的get仍会被调用——这点常被误认为“没生效” - 如果目标对象被冻结(
Object.freeze()),Proxy 依然有效,但要注意set拦截器可能因目标不可写而失效
真正麻烦的是嵌套对象:Proxy 只代理一层,深层属性访问不会自动递归代理。需要手动 wrap 子对象,或在 get 里判断返回值是否为对象并返回新 Proxy ——但这会带来性能开销和循环引用风险。

















