使用Javascript模块系统的问题我正试图制作并且无法弄清楚

时间:2015-04-08 05:19:26

标签: scope closures javascript

我有一个想法,我真的很喜欢我试图为我的一个项目创建的模块系统,问题是我似乎无法弄明白。这个想法非常简单,没有什么复杂,也很简单,但我想我错过了一些东西,因为我花了几个小时的时间尝试一些东西。

基本上我想要做的就是在模块中有4个变量,public,protected,package和private,以及3个函数augment,extend和submodule。

这个想法是每个模块都是一个匿名函数,它将一个公共接口保存到一个窗口变量中,并让其他所有东西都无法访问。也就是说,公共变量和3个函数中的任何内容都是唯一的公共接口。

理想情况下,通过此设置,您可以使用扩充函数直接扩充任何模块,使用扩展函数将模块扩展为父/子关系,并使用子模块函数在该模块上创建子模块。根据所选的变量和方法,可以访问变量,例如,所有子模块和扩展都可以访问包,而子模块无法访问受保护的文件。

这应该形成一个链,每个子模块向下或每个扩展可以访问其父数据,并有空间添加自己的子数据。我还应该注意,意图不像一个类,其中每个实例都有自己的所有变量的副本,而不是每个模块中只有一个变量池,它们根据所采取的行动相互链接。

我在下面提到的是我到目前为止所提出的但是它不太合适并且不太合适,因为它倾向于更类似的设置并且实际上没有像我上面所描述的那样工作但是应该让你知道我在说什么。

非常感谢任何帮助,代码和jsfiddle链接在下面

https://jsfiddle.net/0ghbxdpo/

function Module()
{
    // Accessible to everything
    this._public = {};

    // Accessible to all extensions and submodules
    this._package = {};

    // Accessible only to extensions of the same module
    this._protected = {};

    // Accessible only to augmentations
    this._private = {};
}

// Augments this module and returns the newly augmented module, using
// the return value isn't nesasary as the actual module would have successfully
// been augmented
// Access to all properties
Module.prototype.augment = function augment(augmentation)
{
    return augmentation({
        _public: this._public,
        _package: this._package,
        _protected: this._protected,
        _private: this._private
        });
}

// Create a new module with this one as its parent
// Access to all but this modules private variables
Module.prototype.extend = function extend(extension)
{
    var obj = new Module();

    obj._public._parent = this._public;
    obj._package._parent = this._package;
    obj._protected._parent = this._protected;

    // Execute the extension module under the context of a new module
    // with all but the private variables as its parents
    return extension.call(obj);
}

// Create a new module and assign it to one of the 4 scope variables, public by default
// Access only to public and package modules
Module.prototype.submodule = function submodule(moduleName, module, scope)
{
    var obj = new Module();

    obj._public._parent = this._public;
    obj._package._parent = this._package;

    // Run the submodule code under the context of the new module
    var tmp = module.call(obj);

    // Then assign it to the correct scope variables
    if(scope === "protected")
        return this._protected[moduleName] = tmp;
    else if(scope === "package")
        return this._package[moduleName] = tmp;
    else if(scope === "private")
        return this._private[moduleName] = tmp;
    else
        return this._public[moduleName] = tmp;
}

// Some test to play around and test it out
// This isnt ideal as theres no way to actually setup anything with the initial module
// Also it reveals all the scopes to the public
window.CoolTest = new Module();

// This fails with an error as "this" points to the wrong object so the whole
// thing is created wrong and fails. You cant use call, apply, or bind from here as
// the public should not have access to anyting but the public variables
// The other scope variables are supposed to be stored internally and automatically
// used to create the submodule without ever exposing them
window.CoolTest.submodule("sub", function()
                       {
                           this._public.hello = "world";
                       });

1 个答案:

答案 0 :(得分:0)

我想通了,完整的代码在这个新更新的jsFiddle链接中,在它下面的文本中已完成的模块系统。

https://jsfiddle.net/0ghbxdpo/1/

/*
 * Module System
 * This is not intended to be a class emulation nor will it work very well if
 * treated like one.
 *
 * This was created out of desire for sharing properties and functions among
 * different components in a program. One where permissions to all of it is
 * very fine grained and strictly enforced.
 *
 * It was also created out of desire for lazy loading and lazy augmentation. In
 * other words, I want to be able to split a large module into several files
 * and have it just work. I also want each module to be augmentable and
 * extendable still supporting the lazyloading approach and still respecting
 * permissions
 *
 * You create a module by instiating this class, by doing so you will get an
 * empty module. You may also provide a function as parameter to do an initial
 * custom setup, the function will be passed an internal private object which
 * contains the actual class so you can modify where fit. You have access to the
 * entire class. A second parameter may be passed for special cirumstances where
 * you want to access some variable inside the constructor function you provide.
 *
 * After the module is created it returns the public interface, this interface
 * contains only the public variable contents and 3 methods that work on the
 * module. There is intentionally no way to access the full module as it remains
 * closed off tightly to the public.
 *
 * You may perform 3 actions on the module.
 * "_augment" which re-gives you full control over the module and all of its
 *      properties.
 *
 * "_extend" which creates a new module that contins "_parent" links to the
 *      parents public, package, and protected variables. You can directly edit
 *      the parent object or you can add your own properties outside of the
 *      parent. Any changes you make to the parent will be reflected in the
 *      parent while any chnages you make outside of the parent will not be
 *      accessible by it
 *
 * "_submodule" which does what extend does except that instead of leaving out
 *      just the parents private variables, it also leaves out the parents
 *      protected variables. Not only does it do this, but it also ammends
 *      one of its scope variables, public by default, and attaches the modules
 *      public interface to it.
 */
var Module = (function ()
{
    // initFunc is the otpional configuration function
    //      It is passed the internal module object and scopeRef if provided
    // scopeRef is purely used for passing a custom parameter to the init function
    function Module(initFunc, scopeRef)
    {
        // Declare 4 scope variables
        // Augmentation can always modify these values
        this._public = {}; // Accessible to anyone
        this._package = {};     // Accessible to extensions and submodules
        this._protected = {};   // Accessible only to extensions
        this._private = {};     // Not accessible anywhere

        // Scope related reasons
        this._self = this;

        // This is a bit of trickory to allow a public interface entirely closed
        // off from the actual module, it pre-executes and saves the contents
        // to be used later
        // the parameter is the trick in how we access the real module
        this._publicReturn = function publicReturn(self)
        {
            // We prepare the public interface by directly referencing the modules
            // public variable
            var ret = self._public;

            // Then we ammend the interface and the public variable by adding in
            // the 3 methods that can be used to work on the real internal module
            ret._augment = self.augment.bind(self);
            ret._extend = self.extend.bind(self);
            ret._submodule = self.submodule.bind(self);
            return ret;
        }(this);

        // Now that thats all out of the way, we process the given init function
        // if provided
        if (initFunc)
            initFunc(this, scopeRef);

        // And we return only the public interface
        return this._publicReturn;
    }

    // This modifies the module by re-opening up the entire module for any
    // kind of modification, works much the same way as the constructor
    Module.prototype.augment = function (initFunc, scopeRef)
    {
        initFunc(this, scopeRef);
        return this._publicReturn;
    };

    // This creates a brand new module with parent references to this
    // modules public, package, and protected variable scopes
    Module.prototype.extend = function(initFunc, scopeRef)
    {
        // Start with blank module
        var obj = new Module();

        // Augment in the parent references
        obj._augment(function(_obj, _scopeRef)
                   {
                       _obj._public._parent = _scopeRef._public;
                       _obj._package._parent = _scopeRef._package;
                       _obj._protected._parent = _scopeRef._protected;
                   }, this);

        // Allow the user to do augmentation if provided
        if(initFunc)
            obj._augment(initFunc, scopeRef);

        // Return the given public interface
        return obj;
    };

    // This creates a brand new module with parent references to this
    // modules public and package variable scopes. It also ammends the newly
    // made module to one of this modules variable scopes, by default public.
    Module.prototype.submodule = function (moduleName, initFunc, scopeRef, scope)
    {
        // Start with blank module
        var obj = new Module();

        // Augment in the parent references
        obj._augment(function(_obj, _scopeRef)
                   {
                       _obj._public._parent = _scopeRef._public;
                       _obj._package._parent = _scopeRef._package;
                   }, this);

        // Allow the user to do augmentation if provided
        if(initFunc)
            obj._augment(initFunc, scopeRef);

        // Then assign it to the correct scope variables
        if (scope === "protected")
            return this._protected[moduleName] = obj;

        else if (scope === "package")
            return this._package[moduleName] = obj;

        else if (scope === "private")
            return this._private[moduleName] = obj;

        else
            return this._public[moduleName] = obj;
    };

    return Module;
})();