使用具有显示模块模式的 ECMAScript 5 get/set 属性

Using ECMAScript 5 get/set properties with revealing module pattern

考虑以下 contrived bit of JavaScript based on the revealing module pattern

var RevealingModuleSample = function () {
    var moduleName = "DefaultModuleName",

    init = function (name) {
        moduleName = name
    },

    issueAlert = function () {
        alert(moduleName + " module is running");
    }

    return {
        init: init,
        issueAlert: issueAlert
    };
} ();

RevealingModuleSample.init("SampleModule");

RevealingModuleSample.issueAlert();

有没有办法使用 getter 和 setter 方法 将 moduleName 转换为 属性 并让内部调用那些 getter/setter 方法引用 moduleName? 举一个具体的例子,考虑以下 similarly contrived C# example:

public class ContrivedCsharpExample
{
    private string _moduleName;

    protected string moduleName
    {
        get {
            // Perhaps some logging code here that records the moduleName was retrieved.
            return this._moduleName;
        }
        set {
            // Some code here that ensure that a valid moduleName was passed.
            this._moduleName = value;
        }
    }

    public void init(string moduleName) {
        this.moduleName = moduleName;
    }

    public void issueAlert() {
        Console.WriteLine(this.moduleName);
    }


    public static void Main()
    {
        var module = new ContrivedCsharpExample();

        module.init("SampleC#Module");
        module.issueAlert();
    }
}

请注意,在 class 内部,这些方法正在调用具有 getter/setter 的属性。这允许我们在那些 getter/setter 方法中添加额外的代码,而如果我们只使用 class 之外可用的属性,"init" 和 "issueAlert" 方法将不得不直接检查该字段以及我们添加到 getter/setter 的任何逻辑都将被绕过。

我看到的问题是,在揭示模块模式中,"moduleName" 变量不是对象上的 属性——它只是一个当前在范围内的变量。因此,您不能只是将其拉出并用 get/set 对替换它。对吗?

There is a related question on Whosebug,但在那种情况下,发问者正在寻找一种方法来 return 具有 属性 和 getter/setter 代码,以便模块的外部用户通过 get/set 方法路由他们的 属性 访问。问题中列出了一个解决方案,但提供的解决方案不允许模块内部的代码访问 属性 而不直接对变量进行操作,绕过 get/set 函数。

我认为这对于显示模块模式来说根本不可能,对吗?有没有办法让模块内部的调用通过 get/set 函数路由?

你是对的,因为你不能在变量上定义 setter/getter。它必须是对象的 属性。解决方案是将模块的内部转换为一个对象,同时仍然暴露相同的 public API:

var RevealingModuleSample = function () {

    var self = {

        _moduleName: "DefaultModuleName",

        set moduleName(name) {
            console.log("setting module name: " + name);
            self._moduleName = name;
        },

        get moduleName() {
            return self._moduleName;
        },

        init: function (name) {
            self.moduleName = name;
        },

        issueAlert: function () {
            alert(self.moduleName + " module is running");
        }

    };

    return {
        init: self.init,
        issueAlert: self.issueAlert
    };
}();

RevealingModuleSample.init("SampleModule");
RevealingModuleSample.issueAlert();

公开对象方法的一个后果是,在调用方法时,this 的上下文或值会丢失。因此在对象内部使用 self,这是对 this.

的简单引用

我想另一种方法是将所有私有变量存储在一个单独的对象中,但这对我来说似乎不太优雅。

是一种使用 IIFE 来接近您想要的东西的方法,尽管我认为它有点矫枉过正。但是,您必须做出妥协——您总是必须使用函数而不是使用属性:

var Example = function () {
    var moduleName = (function(moduleName){
    return function(value){
        if (val === undefined) return moduleName;
        moduleName= val;
    };
    })("DefaultModuleName");

    init = function (name) {
        moduleName(name);
    },

    issueAlert = function () {
        alert(moduleName() + " module is running");
    }

    return {
        init: init,
        issueAlert: issueAlert
    };
} ();

Example.init("SampleModule");

Example.issueAlert();

诀窍是使用 IIFE return 访问函数。如果不带参数调用,该函数是 getter,如果带参数调用,它是 setter。访问函数改变了 IIFE 闭包中隐藏变量的值,IIFE 之外的任何东西都无法访问它,除非通过访问函数。

在这种情况下,揭示模块模式是一个转移注意力的问题 -- 它与问题没有任何关系,它是一个 anti-pattern as it's the worst of the implementations of the module pattern