JavaScript,proxy和reflect读取类私有属性的实践对比
发布于 作者:苏南大叔 来源:程序如此灵动~
本文通过对类私有属性的读取实验,简单的对比一下proxy和reflect在读取类属性时的区别所在。当然,这个对比并不能完全的说明两者的差别,仅仅是个抛砖引玉的效果。

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验文章。本文测试环境:win10,chrome@116.0.5845.141,node@16.14.2。
正常读取类私有属性
正如上一篇文章里面,参考:
苏南大叔得出的结论:
正常情况下来说,是不能读取到类的私有属性的。除非对应类主动开放了一个读取私有属性的方法,作为口子。参考代码:
class nan {
#name = "类真实私有属性";
get name() {
console.log("只有类自身内部的方法能访问");
return this.#name;
}
}
nan = new nan();
console.log(nan["#name"], nan["name"], nan.name); // undefined 类真实私有属性 类真实私有属性这里的get name()是本文生效的重要前提。因为私有属性是除了自身外无论如何都不能读取的。所以,必须有个方法将其暴露出去才能读取。

reflect读取
下面再使用reflect读取一下试试,测试代码如下:
console.log(Reflect.get(nan,"#name")); // undefined
console.log(Reflect.get(nan,"name")); // 类真实私有属性执行结果是:
undefined
类真实私有属性结论是:reflect和其它的情况一样,无法直接访问私有属性。但是可以通过暴露的get name()拿到数据。

Proxy正常可以调用
这里
su = new Proxy(nan, {
get(obj, prop, receiver) {
console.log("代理变量 + obj元素访问 ...");
return obj[prop];
},
});
console.log(su["#name"], su["name"], su.name); // undefined 类真实私有属性 类真实私有属性输出:
代理变量 + obj元素访问 ...
代理变量 + obj元素访问 ...
只有类自身内部的方法能访问
代理变量 + obj元素访问 ...
只有类自身内部的方法能访问
undefined '类真实私有属性' '类真实私有属性'可见:proxy使用obj[prop]的方式成功拿到了数据。

在proxy里面使用reflect
常理上来说,在proxy里面使用reflect一点问题都没有。但是,在读取私有属性这件事情上来说,确产生了较大的运行结果差别。测试代码:
da = new Proxy(nan, {
get(obj, prop, receiver) {
console.log("代理变量 + Reflect反射访问 ...");
// return Reflect.get(obj, prop); // 成功
// return Reflect.get(obj, prop, obj); // 成功
console.log(...arguments) // 这里的 receiver 是代理本身...
return Reflect.get(...arguments); // Cannot read private member #name from an object whose class did not declare it
},
});
console.log(da["#name"]); // undefined
console.log(da["name"]); // 主要看`reflect`咋写的了...在proxy的get()里面,有三种使用reflect的方法,其最主要的区别是receiver不同。如果对Reflect的receiver传递了proxy默认的receiver,读取私有属性这事就失败了。(但是读取公有属性的事情,是可以的。要不怎么是代理呢?)

试图直接使用receiver
da = new Proxy(nan, {
get(obj, prop, receiver) {
console.log("代理变量 + Reflect反射访问 ...");
return receiver[prop]; // 死循环....
},
});
console.log(da["#name"]); // undefined
console.log(da["name"]);代码死循环了,吓!
表格对比
在proxy里面的get()里面,通过类的公有方法读取私有属性。总结如下:
| 方法 | #name | name |
|---|---|---|
| obj[] | undefined | prop |
| obj. | error | prop |
| proxy[] | undefined | prop |
| proxy. | error | prop |
| Reflect.get(obj,) | undefined | prop |
| proxy.get() + obj[] | undefined | prop |
| proxy.get() + Reflect.get(obj, prop) | undefined | prop |
| proxy.get() + Reflect.get(obj, prop, obj) | undefined | prop |
| proxy.get() + Reflect.get(...arguments) | undefined | error |
| proxy.get() + receiver[] | 死循环 | 死循环 |
课后题
下面的代码是官方自带的例子,猜猜看,输出是什么?
class Secret {
#secret;
constructor(secret) {
this.#secret = secret;
}
get secret() {
// \d+表示1个或多个0到9的数字,是整数部分(至少是一位整数的整数部分)
return this.#secret.replace(/\d+/, "[REDACTED]");
}
}
const aSecret = new Secret("文字部分不会被替换,123456");
console.log(aSecret.secret); // 文字部分不会被替换,[REDACTED]结束语
从本文的代码上来看,proxy和reflect也不是个“水火不容,相互替代”的关系。两者其实也是有合作的可能性的,值得注意的就是proxy的receiver指代的是其代理自身,所以一些情况下,并不适合作为receiver的改写传递到reflect里面。
更多苏南大叔的node经验文章,请点击: