首页 > 解决方案 > 为什么我的单例模块似乎有两个实例?

问题描述

问题

我正在尝试创建一个 Singleton 来存储只能使用 Setter 和 Getter 访问的信息。我喜欢让我的代码尽可能简短和干净,所以我一直在寻找最短的编写方式。虽然这在大多数地方都有效,但在其他地方,我倾向于制造一些(对我来说)不太可解释的错误。

所以,.. 导入我的模块进行测试时,我使用:

const { ports, instance: activeConfig } = require('./activeConfig')

ports应该是我的吸气剂。我想避免activeConfig.ports在可能的情况下总是写出来。在我尝试重置我的值之前,我认为大部分情况下这在我的测试中是有效的。

我观察到的几件事:

  1. 当我运行时,activeConfig.ports我的吸气剂被激活。我可以在记录结果 1、2 和 3 之前看到这一点。
  2. 当我运行时,ports我的吸气剂没有被激活。我可以从日志结果 1a、2a 和 3a 中看到单例内部没有运行任何内容。但我仍然得到结果。
  3. 当我设置一个新值时,我可以在 Singleton 中看到我的值_myPorts确实发生了变化。这两种方法的结果也如我所料。

但是,在我重置我的值后,这种行为不再保持一致。虽然activeConfig.ports提供了预期的结果,ports但没有。我不确定日志 3a 从何处获得此结果以及为什么它与日志结果 3 不同。

我显然已经建立了某种不稳定因素,但我的 JavaScript 技能还不足以查明问题所在。我尝试搜索“JavaScript Singleton Getter Export”的所有变体,但似乎找不到涵盖我特定用例的示例。

如果有人可以帮助我修复我的代码,将不胜感激。此外,像正确的术语或高级示例的链接这样的小东西真的会很感激。:)

我的主要重点是能够在单个 require 命令中导入 getter!
我已经简化了这个例子,但我希望能够用 x 个吸气剂来做到这一点。

我不是在寻找解决方法,例如:

const activeConfig = require('./activeConfig')
const ports = activeConfig.ports

或者

const ports = require('./activeConfig').ports

这需要每个吸气剂一行,我试图避免这种确切的情况。(尤其是 10 多个吸气剂)

我真的很想知道是否有这样做的正确方法。:)

编码

'use strict'
let activeConfig = (function () {
  const _defPorts = {
    http: 80,
    https: 443,
    secure: false
  }

  let _myPorts = {..._defPorts}

  let _setPorts = function (value) {
    console.log('SP->', _myPorts)
    if (value) {
      Object.keys(value).forEach((key) => {
        if (typeof _myPorts[key] !== 'undefined') {
          _myPorts[key] = value[key]
        }
      })
    }
    console.log('SP=>', _myPorts)
  }

  let _resetConfig = function () {
      console.log('RC->', _myPorts)
      _myPorts = {..._defPorts}
      console.log('RC=>', _myPorts)
    }


  return {
    setPorts: _setPorts,
    resetConfig: _resetConfig,
    get instance() {
      return activeConfig 
    },
    get ports() {
      console.log('GP->', _myPorts)
      return _myPorts
    },
    set ports(value) {
      return _setPorts(value)
    }
  }
})()
module.exports = activeConfig

测试代码

'use strict'
const {
    ports,
    instance: activeConfig
} = require('./activeConfig')

console.log('1 ->', activeConfig.ports)
console.log('1a->', ports)
activeConfig
    .setPorts({
        secure: true,
        http: 8080
    })

console.log('2 ->', activeConfig.ports)
console.log('2a->', ports)
console.log('RESET')
activeConfig.resetConfig()
console.log('3 ->', activeConfig.ports)
console.log('3a->', ports)

日志

GP-> { http: 80, https: 443, secure: false }
GP-> { http: 80, https: 443, secure: false }
1 -> { http: 80, https: 443, secure: false }
1a-> { http: 80, https: 443, secure: false }
SP-> { http: 80, https: 443, secure: false }
SP=> { http: 8080, https: 443, secure: true }
GP-> { http: 8080, https: 443, secure: true }
2 -> { http: 8080, https: 443, secure: true }
2a-> { http: 8080, https: 443, secure: true }
RESET
RC-> { http: 8080, https: 443, secure: true }
RC=> { http: 80, https: 443, secure: false }
GP-> { http: 80, https: 443, secure: false }
3 -> { http: 80, https: 443, secure: false }
3a-> { http: 8080, https: 443, secure: true }

标签: javascriptnode.jssingletongetter-setter

解决方案


对象解构只是属性访问的语法糖:

 const { port } = obj;
 // equals
 const port = obj.port;

因此,在您解构时,您确实可以访问 getter。更改port会更改局部变量。它不会反映到对象上,因此不会触发 setter。

没有真正的方法以这种方式使用 getter / setter(好吧,让我们忘记吧with)。

相反,您可以手动触发虚幻设置器:

  const obj = {
    port: { set(_v) { return this._v = _v; }, get() { return this._v; } }
  };

 const { port } = obj;
 port.set(10);

推荐阅读