JavaScript 模块模式变量范围

javascript module pattern variable scope

本文关键字:范围 变量 模式 模块 JavaScript      更新时间:2023-09-26

我正在学习javascript和模块模式,但我在代码中犯了一个错误,事实证明它对这种模式的一些概念是正确的。我的基本代码是这样的:

(function(window,$){
//global menu object
var menu = (function(){
    //menu tab component
    var tab = (function(){
        //public properties
        var tab = {
            init:doStuff
        }
        //private properties
        function doStuff(){
            alert("initialising Tab")
        }
        //return public properties
        return tab;
    })();
    //menu curtain component
    var curtain = (function(){
        //public properties
        var curtain = {
            init:doStuff
        }
        //private properties
        function doStuff(){
            alert("initialising Curtain")
        }
        //return public properties
        return curtain;
    })();
    //menu content component
    var content = (function(){
        //public properties
        var content = {
            init:doStuff
        }
        //private properties
        function doStuff(){
            alert("initialising Content")
        }
        //return public properties
        return content;
    })();
    //public properties
    var menu = {
        init:initialiseMenu
    }
    //private properties
    function initialiseMenu(){
        //initialise each component of the menu system
        tab.init();
        curtain.init();
        content.init();
    }
    //final menu object
    return menu;
})();
window.menu = menu;
})(window,jQuery);

然后,当我的页面加载并调用代码时:

menu.init();

它按顺序发出警报:

initialising tab
initialising curtain
initialising content

正如我所料。但是如果我将内容组件更改为如下所示:

   //menu content component
    var content = (function(){
        //public properties
        var content = {
            init:doStuff
        }
        //private properties
        function doStuff(){
            alert("initialising Content")
        }
        //CHECK ACCESS TO PREVIOUS VARIABLES
        curtain.init();
        //return public properties
        return content;
    })();

它按顺序发出警报:

initialising curtain
initialising tab
initialising curtain
initialising content

所以我看到它能够访问 curtain 变量,即使它没有作为参数传递到模块中。我以为每个模块都是自包含的,但我发现事实并非如此,无论如何可以让模块也只能访问您想要的变量吗?特别是对我的例子会有所帮助,谢谢丹。

每个模块都不是自包含的,而是创建一个新作用域,该作用域是创建该模块的超集。在 Javascript 中定义新作用域的唯一内容是 function 语句。在新作用域中,外部作用域中的所有内容都是可见的,除非被同名的变量覆盖。内部范围内的任何内容都对它之外的东西是可见的。

var global;
function outer() {
    var outerVar;
    function inner() {
        var innerVar;
        // global, outerVar, and innerVar are visible
    }   
    function inner2() {
        var inner2var, outerVar;
        // global and inner2var are visible
        // outerVar hides the previous outerVar, which is no longer accessible
    }
    // global and outerVar (the first one) are visible
} 

您的函数是自执行的事实并没有区别。在外部作用域中创建的任何内容都将在内部作用域中可见,除非您创建一个同名的新局部变量来取代它。

就您的内部范围而言,在其外部创建的任何内容都与全局范围大致相同。(全局只是在默认范围内创建的变量,即浏览器中的"窗口")。

你可以把一个内在的范围想象成在单向玻璃后面。你仍然可以看到世界上的一切,但世界看不到你。而且你总是可以选择挡住单向玻璃,这样你就再也看不见外面了。但没有什么能看到里面。

任何函数的当前作用域都可以看到它的包含作用域。 因此,内容仍然可以访问菜单中的任何变量,其中包括窗帘。

发生这种情况是因为当您在每个对象中调用"return"时,您将返回值分配给组件变量,该值是每个组件中的内部"公共属性"对象。

var tab = (function(){
    //public properties
    var tab = {
        init:doStuff
    }
    //private properties
    function doStuff(){
        alert("initialising Tab")
    }
    //return public properties
    return tab;
})();

在这里,您将原始变量"tab"与正在执行的匿名函数的结果一起分配。本例中的对象是:

    var tab = {
        init:doStuff
    }

因为您在函数执行结束时返回此对象。

若要实现所追求的目标,请尝试返回一个具有"public"修饰符函数的对象,该修饰符函数访问函数范围内的变量。在函数中创建的任何变量都只具有该函数或其作用域内的函数的作用域,从而使它们有效地私有(Javascript 在功能上是作用域的)。以下示例应该可以帮助您处理代码:

var tab = (function(){
    //public properties
    var PublicProperties = {
        init:doStuff,
        modifyPrivateVar: function(value){
            this.somePrivateVariable = value;
        }
    }
    //private variables/properties
    var doStuff = function(){
        alert("initialising Tab")
    }
    var somePrivateVariable = 'private!';
    //return public properties
    return PublicProperties;
})();

现在,您的变量"tab"将通过执行匿名函数为返回的值(PublicProperties 对象)分配。你可以访问函数"init"和"modifyPrivateVar",但你不能直接调用"doStuff"或更改"somePrivateVariable"。这表明您可以通过修饰符函数更改变量,但不能直接访问它,从而使它有效地私有。

如果您希望将"init"函数作为构造函数调用,则必须在组件的匿名函数执行中执行"

构造函数",或者只是内联编写代码,它将在组件的匿名函数执行时执行。否则,如果 init 函数是私有的,则不应返回与该函数相关的任何内容,而只返回可用于以安全方式修改/激活对象的函数。