如何判断引用是否在JavaScript中发布

时间:2019-03-20 15:14:12

标签: javascript ecmascript-6

我正在尝试重构使用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!

从代码安全性和内存管理的角度来看,存在相同的问题,哪种解决方案更合适?这些是身份验证类,因此我想确保它们被正确收集并且不会被滥用。

3 个答案:

答案 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成为孤儿,这意味着它将被垃圾收集器删除

有关更多信息,请阅读https://javascript.info/garbage-collection

答案 2 :(得分:1)

在Javascript中,GC并不是潜在地滥用对象中可用信息的大问题。它是对象本身。借助现代开发人员工具,除非混淆不清,否则您可以轻松进入前端代码的任何部分并从中受益。海事组织,近来非常需要混淆。第一个减小了文件的大小,第二个减小了在生产环境中使用代码的难度。

现在开始讨论实际问题。将新实例new Auth分配给client后,client不再硬引用旧实例,因此只要不保留其他引用,就可以进行垃圾回收。无法保证内存回收的速度。

使用let的优势在于它的作用域。它仅限于其块。但是,有巨大的障碍并不少见。与全局变量相比,let为您提供了一个较小的范围,因此可能在块结束后不久被释放。也可能是Javascript运行时可能将方法堆栈用于let变量的情况,一旦块结束(方法),它将丢弃堆栈,因此引用也将被丢弃。

最后,按原样进行是绝对好的,并且您的实现没有比上一个有任何优势。