我正在尝试重构使用let
的代码,以声明模块范围内的Auth实例,该实例随后由于配置更改而被重新分配(在同一模块中)。原始实现如下所示。
let client = new Auth({ config });
// ...later in the same module
export const updateConfig = (_config) => { client = new Auth(_config) };
我的第一个问题是updateConfig()
之后发布的原始客户端。您将如何证明?
这种方法有什么缺点吗?
我提出的重构旨在通过使用隐式构造函数将Auth模块包装在一个单例中来使它的魔术性降低一点。但是,它需要实例的吸气剂。但是,从本质上讲,它通过应用新配置时重新分配引用来完成相同的功能。
function Client(options) {
if (Client._instance) {
return Client._instance;
}
if (!(this instanceof Client)) {
return new Client(options);
}
this._instance = new Auth(options);
}
Client.prototype.getInstance = function() { return this._instance };
Client.prototype.updateConfig = function(opts) {
this._instance = new Client(opts).getInstance();
}
// usage
const client = new Client({ foo: 'bar'});
client.updateConfig({bar: 'baz'});
console.log(client.getInstance()); // eg. { bar: 'baz' } works!
从代码安全性和内存管理的角度来看,存在相同的问题,哪种解决方案更合适?这些是身份验证类,因此我想确保它们被正确收集并且不会被滥用。
答案 0 :(得分:3)
我的第一个问题是,原始客户端是在updateConfig()之后发布的吗?
也许client
是唯一引用它的变量。
您将如何证明这一点?
在控制台中进行内存转储,然后搜索那些客户端对象。
这种方法是否有缺点?
否,只要没有人引用您希望更新的客户端即可
const referenced = client;
updateConfig();
console.log(referenced === client); // false
我提出的重构的目的是使它变得不那么神奇了……但是,实质上,它做同样的事情
如果将更改隐藏在20行代码的后面,为什么它“那么不可思议”?如果我要担任审阅者,我将拒绝此更改,因为它会带来一些意外的行为,并且毫无益处:
console.log(new Client === new Client); // true, wtf
我将如何重构(低估了好的评论):
// Note: client gets re-set in updateConfig(), do not reference this!
let client = new Auth({ config });
从代码安全和内存管理的角度来看,哪种解决方案更合适?
“但是,实质上,它执行相同的操作”。明智的话。
答案 1 :(得分:1)
当我们在构造函数上调用new
时,它将始终返回一个新对象,这意味着当client
后来被突变时,它现在肯定拥有新值。那是一回事。
另一件事是,JavaScript运行时环境垃圾回收器会查找内存中但未从任何变量引用的对象,如果找到,则将其删除。
所以基本上当我这样做
let obj = {name: 'Hello'}
obj
指的是某个具有2ABCS
内存地址的对象,当我对其进行突变时
let obj = {name: 'World'}
现在它指向地址为1ABCS
的对象,该对象使2ABCS
成为孤儿,这意味着它将被垃圾收集器删除
答案 2 :(得分:1)
在Javascript中,GC并不是潜在地滥用对象中可用信息的大问题。它是对象本身。借助现代开发人员工具,除非混淆不清,否则您可以轻松进入前端代码的任何部分并从中受益。海事组织,近来非常需要混淆。第一个减小了文件的大小,第二个减小了在生产环境中使用代码的难度。
现在开始讨论实际问题。将新实例new Auth
分配给client
后,client
不再硬引用旧实例,因此只要不保留其他引用,就可以进行垃圾回收。无法保证内存回收的速度。
使用let
的优势在于它的作用域。它仅限于其块。但是,有巨大的障碍并不少见。与全局变量相比,let
为您提供了一个较小的范围,因此可能在块结束后不久被释放。也可能是Javascript运行时可能将方法堆栈用于let
变量的情况,一旦块结束(方法),它将丢弃堆栈,因此引用也将被丢弃。
最后,按原样进行是绝对好的,并且您的实现没有比上一个有任何优势。