
在使用JavaScript Proxy包装第三方库函数时,开发者可能会在开发环境运行正常,但在生产构建中遭遇TypeError,尤其涉及Reflect.get的receiver参数。本文深入探讨了Proxy#get和Reflect.get中receiver参数的正确用法,解释了为何不当使用会导致生产环境报错,并提供了正确的代码实践,以确保Proxy行为在所有环境中一致且健壮。
在JavaScript中,Proxy对象允许你拦截并自定义对目标对象的各种操作。其中,get陷阱(trap)用于拦截属性读取操作。当一个属性被读取时,Proxy的get方法会被调用,其签名如下:
get(target, property, receiver)
在get陷阱内部,我们常常会使用Reflect.get来获取目标对象的原始属性值。Reflect.get的签名如下:
Reflect.get(target, propertyKey, receiver)
当尝试使用Proxy来修改MUI的styled函数时,原始代码示例如下:
import * as muiSystem from '@mui/system';
type CreateMUIStyled = typeof muiSystem.styled;
type MUIStyledParams = Parameters<CreateMUIStyled>;
const system = new Proxy(muiSystem, {
get(target, prop, receiver) {
if (prop === 'styled') {
// 问题所在:将Proxy的receiver直接传递给了Reflect.get
const styled = Reflect.get(target, prop, receiver);
return function (...args: Parameters<CreateMUIStyled>) {
const newOptions = getComposedOptions(args[1]);
const newArgs: MUIStyledParams = [...args];
newArgs.splice(1, 1, newOptions);
const styledResult = styled(...newArgs);
return function (
...newestArgs: Parameters<ReturnType<CreateMUIStyled>>
) {
return styledResult(...newestArgs);
};
};
}
// 原始代码此处返回undefined,这会导致访问其他属性时失败
return Reflect.get(target, prop, receiver); // 应该处理其他属性的访问
},
});
export const styled = system.styled;这段代码在开发环境中可以正常运行,但在生产环境中却抛出了一个TypeError:
Uncaught TypeError: 'get' on proxy: property 'styled' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '(o,s={})=>{n6e这个错误信息非常关键,它指出Proxy的get陷阱未能返回目标对象muiSystem上styled属性的“实际值”。这通常发生在以下情况:
在原始代码中,Reflect.get(target, prop, receiver)这一行是问题的核心。这里的receiver是Proxy实例本身。当Reflect.get尝试从target(即muiSystem)获取styled属性时,如果styled是一个需要特定this上下文(例如muiSystem本身)的getter或方法,而你传入了Proxy实例作为receiver,那么styled内部的逻辑可能会因为this上下文不匹配而失败。对于一些只读且不可配置的属性,JavaScript引擎会更严格地检查Proxy的get陷阱是否返回了与目标属性完全相同的值。如果Reflect.get由于receiver不正确而未能正确地“激活”或获取到styled的真实行为,就会触发此错误。
为了确保Reflect.get能够正确地获取到目标属性的值,尤其当该属性是getter或方法时,其receiver参数应该指向目标对象本身,或者一个能够正确模拟目标对象this上下文的对象。
正确的做法是将target(即muiSystem)作为Reflect.get的第三个参数:
Reflect.get(target, propertyKey, target)
这样可以保证当styled属性被访问时,如果它是一个需要this上下文的函数,其this将正确地绑定到muiSystem对象上,从而避免TypeError。
基于上述分析,对Proxy的get陷阱进行修改,确保Reflect.get使用正确的receiver:
import * as muiSystem from '@mui/system';
type CreateMUIStyled = typeof muiSystem.styled;
type MUIStyledParams = Parameters<CreateMUIStyled>;
const system = new Proxy(muiSystem, {
get(target, prop, receiver) {
if (prop === 'styled') {
// 修正:将target作为Reflect.get的receiver参数
const styled = Reflect.get(target, prop, target);
// 确保 styled 是一个函数,以防万一
if (typeof styled !== 'function') {
console.warn(`muiSystem.styled is not a function. Prop: ${String(prop)}`);
return styled; // 返回原始值
}
return function (...args: Parameters<CreateMUIStyled>) {
// 假设 getComposedOptions 存在且逻辑正确
const newOptions = getComposedOptions(args[1]);
const newArgs: MUIStyledParams = [...args];
newArgs.splice(1, 1, newOptions);
const styledResult = styled(...newArgs);
return function (
...newestArgs: Parameters<ReturnType<CreateMUIStyled>>
) {
return styledResult(...newestArgs);
};
};
}
// 对于其他属性,也应该使用Reflect.get并传递target作为receiver
return Reflect.get(target, prop, target);
},
});
export const styled = system.styled;
// 假设 getComposedOptions 函数的定义
function getComposedOptions(options: any) {
// 实现你的自定义逻辑
return { ...options, someCustomProperty: true };
}代码解释:
这种开发环境正常、生产环境报错的现象在前端开发中并不少见,通常有以下几个原因:
以上就是解决生产环境中Proxy与Reflect.get引发的TypeError的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号