【问题标题】:Issue with Javascript module system I'm trying to make and cant figure out我正在尝试制作但无法弄清楚的Javascript模块系统问题
【发布时间】:2015-04-08 05:19:26
【问题描述】:

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

基本上我想要做的就是在一个模块中拥有 4 个变量,公共的、受保护的、包的和私有的,以及 3 个函数增强、扩展和子模块。

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

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

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

我下面的内容是我到目前为止提出的内容,但它并不完全正确,也不太适合,因为它更倾向于像 setup 这样的类,并且真的不像我想要的那样工作,如上所述但是应该让你明白我在说什么。

非常感谢任何帮助,下面的代码和 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";
                       });

【问题讨论】:

    标签: scope closures javascript


    【解决方案1】:

    我想通了,完整的代码在这个新更新的 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;
    })();
    

    【讨论】:

      猜你喜欢
      • 2015-01-08
      • 1970-01-01
      • 1970-01-01
      • 2021-04-08
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 2013-02-14
      • 1970-01-01
      相关资源
      最近更新 更多