【问题标题】:Replace js namespaced global variable with js closure用 js 闭包替换 js 命名空间的全局变量
【发布时间】:2013-05-25 11:05:25
【问题描述】:

我在一个单页网络应用程序中有一些共享代码,该应用程序当前使用 "globals" 命名空间将参数存储为一个全局变量。

使用命名空间是对污染全局“窗口”对象的改进,但似乎这段代码是一个很好的闭包候选,以在调用之间保持值。我搞砸了一些想法,但似乎无法正确理解闭包的语法。

这是当前版本的伪代码。所有代码都位于“um”命名空间中。当我的共享函数最初由我的应用程序中的一个新虚拟页面调用时,我需要存储一个名为“extraData”的 JS 对象的内容。该函数的后续调用无权访问“extraData”,因此如果 underscore.js 确定,我目前将其存储在 "um.globals.extraData" 中参数是一个对象。

//***************************
// IMPLEMENTATION SAMPLE
//***************************

// Define namespaces (not showing: um.grid, um.ajax, um.classes, um.constants, etc.)
window.um = window.um || {};

um.globals = um.globals || {}; /* container for namespaced 'global' variables */

um.grid.loadOrUpdate = function (iOffset, isUpdate, extra) {
    var ajaxParams = new um.classes.AjaxParams();
    //-----

    // If 'extra' is an object, store it in a global for subsequent invocations
    if (_.isObject(extra)) {
        // This seems like it could be a closure candidate...
        um.globals.extraData = extra;
    }

    ajaxParams.values = [um.constants.urlPathParams.grid];
    ajaxParams.verb = um.constants.httpVerbs.GET;

    // Use the global variable 'extraData'
    ajaxParams.extraData = um.globals.extraData;

    um.ajax.callMessaging(ajaxParams);
};

下面是一些实际调用函数的伪代码:

//***************************
// INVOCATION SAMPLES
//***************************

// 1st invocation from virtual page 'Alpha'
um.grid.loadOrUpdate(0, false, { "alpha-key": "alpha-value" });

// 2nd invocation from virtual page 'Alpha'
um.grid.loadOrUpdate(1, true); // will re-use the "alpha" object

// 1st invocation from virtual page "Beta'
um.grid.loadOrUpdate(0, false, { "beta-key": "beta-value" });

// 2nd invocation from virtual page 'Beta'
um.grid.loadOrUpdate(1, true); // will re-use the "beta" object

我怎样才能杀死 um.globals.extraData 并将其替换为 um.grid.loadOrUpdate 内的某种 closure

编辑

下面是“JavaScript 模式”中的一些代码,促使我提出这个问题:

var setup = function () {
    var count = 0;
    return function () {
        return (count += 1);
    }
};

// usage
var next = setup();
next(); // returns 1
next(); // returns 2
next(); // returns 3

【问题讨论】:

    标签: javascript namespaces closures globals


    【解决方案1】:

    对我来说,不清楚你想通过闭包实现什么。闭包允许您封装当前范围内的变量状态,如果您尝试创建对象的各种实例,每个实例都有自己的extra 状态,这可能会很方便。

    您可以通过实现loadOrUpdate 来实现这一点,即返回对稍后可以调用的函数的引用。当调用该函数时,该范围内的所有变量都将被封闭并保留该函数创建时的值。

    例如:

    um.grid.loadOrUpdate = function (iOffset, extra) {
        var ajaxParams = new um.classes.AjaxParams();
        //-----
    
        ajaxParams.values = [um.constants.urlPathParams.grid];
        ajaxParams.verb = um.constants.httpVerbs.GET;
    
        um.ajax.callMessaging(ajaxParams);
    
        // Return a function used to update this data later
        return function (newOffset) // Update function
        {
           // From within here, you'll have access to iOffset and extra as they exist at this point
           window.alert("Key: " + extra.key + " - Changing offset from " + iOffset + " to " + newOffset);
           iOffset = newOffset;
        };
    };
    

    然后你可以像这样调用你的函数,记住它会返回一个函数的引用:

    var alpha = um.grid.loadOrUpdate(0, { "key": "alpha-value" });
    var beta = um.grid.loadOrUpdate(0, { "key": "beta-value" });
    

    当您调用alpha()beta() 时,extra 的值将通过闭包保留,因此无需保留对其的全局引用。

    alpha(1); // Update from 0 to 1
    alpha(2); // Update from 1 to 2
    beta(3); // Update from 0 to 3
    beta(4); // Update from 3 to 4
    

    Example

    但是,如果您尝试保留所有对 loadOrUpdate 的调用共享的 extra 的单个实例,则最好使用以前的技术并将当前值存储为函数本身,或该函数范围内的任何其他位置。

    【讨论】:

    • 我开始认为摆脱我命名空间的“全局”可能不是在“loadOrUpdate”内部关闭的好选择。正如您所指出的,我只需要一个“loadOrUpdate”实例,并且原始实现效果很好。 'um.globals...' 中的值不会污染窗口命名空间,并且名称 'globals' 清楚地表明,存储在其中的任何值都旨在全局范围内的 'um...' 我认为这是一个案例试图过度优化或过度设计已经运行良好的东西。全局变量本质上并不是邪恶的。 :-)
    【解决方案2】:

    这种方法是你所追求的吗?

    var ns = {};
    (function() {
     var globals;
     ns.func = function(update,opts) {
      if(update)opts=globals;
      else globals=opts;
      console.log(opts);
     }
    })();
    
    ns.func(false,"a");
    ns.func(true);
    ns.func(false,"b");
    ns.func(true);
    

    输出:

    a
    a
    b
    b
    

    我已经在一个匿名函数中限定了全局变量的范围,并在该函数中声明的函数在周围(在本例中为窗口)范围内的对象上可用 - 所以它可以访问“全局”变量,但它是在外面看不到。

    【讨论】:

    • 似乎您的实现仍然非常接近我的实现,其中使用“ns.func”之外的全局变量来捕获“opts”。我将使用“Javascript Patterns”中的一个闭包示例更新我最初的问题,这最初让我认为内联闭包是消除全局变量的方法。
    • 这不一样——在函数范围内隐藏一个变量吗?在我的示例中,变量“globals”在全局范围内不可见。这个名字不好,但我保留它是为了表明它在您的原始示例中的用途。变量“globals”隐藏在匿名函数的作用域中。
    • 这是一个参考,以防术语混淆(我自己查了一下!):“内部函数引用其外部函数的局部变量创建闭包”robertnyman.com/2008/10/09/… ...这就是我'在我的示例中,一个由内部函数引用的变量,在外部函数中作用域,在全局作用域中不可见。类似于 Crockford 的私有变量实现 IIRC。
    • 我认为 JavaScript 中的“正确”闭包需要返回一个函数。我的原始实现和您的版本都不返回函数,它们只是将值存储在一个隐藏在全局“窗口”范围内的变量中。我正在考虑某种返回另一个函数的内部函数,例如我添加到问题底部的“设置/下一个”示例。
    • 我认为不同之处在于,在我的示例中,变量 globals 是真正隐藏的,即除了内部函数之外的任何东西都无法访问,而在您的第一个示例中,变量可以通过 @987654325 在全局范围内访问@,它符合我上面链接到的闭包的定义。但我同意你对上面迈克回答的最后评论——你所拥有的可能很好;)
    猜你喜欢
    • 2012-03-13
    • 2011-03-25
    • 2011-10-19
    • 2011-08-31
    • 2017-01-04
    • 1970-01-01
    • 2015-04-22
    • 2012-12-28
    • 2016-04-30
    相关资源
    最近更新 更多