【问题标题】:Passing state to a callback in JavaScript an appropriate use of closures?将状态传递给 JavaScript 中的回调适当使用闭包?
【发布时间】:2010-09-20 01:28:24
【问题描述】:

假设您想在 JavaScript 中发出一个异步请求,但您想将一些状态传递给回调方法。以下是 JavaScript 中闭包的适当用法吗?

function getSomethingAsync(someState, callback) {
    var req = abc.createRequestObject(someParams);
    req.invoke(makeCallback(someState, callback));
}

function makeCallback(someState, callback) {
    return function getSomethingCallback(data) {
        var result = processDataUsingState(data, someState);
        callback(result); // alternately/optionally pass someState along to result
    }
}

如果没有,有没有更好或更惯用的方法?

【问题讨论】:

    标签: javascript closures


    【解决方案1】:

    您还可以使用 call() / apply() 在函数中操作“this”的值。

    例如,指定异步方法的另一个参数是在回调中用作“this”的对象。这就是 jQuery 在事件回调中将 dom 节点设置为“this”的方式。

    function getSomethingAsync(callback, scope) {
        var req = abc.createRequestObject(someParams);
        req.invoke(function(rsp) {
          callback.apply(scope, [rsp]);
        });
    }
    
    // usage:
    getSomethingAsync(function() {console.log(this.someState)}, {someState:'value'});
    

    【讨论】:

      【解决方案2】:

      更惯用的方式是使用Function.bind,这样就不需要复制代码来创建闭包了。我将使用一个简单的示例(没有您的自定义代码)来解释

      /**
       * Retrieves the content of a url asyunchronously
       * The callback will be called with one parameter: the html retrieved
       */
      function getUrl(url, callback) {
          $.ajax({url: url, success: function(data) {
              callback(data);
          }})    
      }
      
      // Now lets' call getUrl twice, specifying the same 
      // callback but a different id will be passed to each
      function updateHtml(id, html) {
          $('#' + id).html(html);
      }
      
      
      // By calling bind on the callback, updateHTML will be called with 
      // the parameters you passed to it, plus the parameters that are used
      // when the callback is actually called (inside )
      // The first parameter is the context (this) to use, since we don't care,
      // I'm passing in window since it's the default context
      getUrl('/get/something.html', updateHTML.bind(window, 'node1'));
      // results in updateHTML('node1', 'response HTML here') being called
      getUrl('/get/something-else.html', updateHTML.bind(window, 'node2'));
      // results in updateHTML('node2', 'response HTML here') being called
      

      Function.bind 是新的,所以如果您需要向后支持,请查看Function.bind 的兼容性部分

      最后,我知道这个问题没有被标记为 jQuery。这只是在不处理跨浏览器问题的情况下显示异步函数的最快方法

      【讨论】:

      • 不错!感谢您指出了这一点。当然,当我在 2008 年第一次写这个问题时,这并不存在。鉴于它仅限 IE9,我想我会在使用它之前稍等片刻......
      • 您不必等待。指向 MDN 的链接包含您需要使其在任何浏览器中工作的代码。
      • 是的,我看到了解决方法,但它有很多注意事项,所以我再次阅读它:-)
      【解决方案3】:

      我没有看到任何直接的问题 - 闭包之所以强大有很多原因,其中之一是无需使用全局变量来维护状态。

      也就是说,关于闭包,您唯一需要注意的是内存泄漏,这通常发生在 IE 中,但通常是 IIRC,与 DOM 元素和附加到其上的事件处理程序有关。

      继续!

      【讨论】:

      【解决方案4】:

      使用匿名函数更好(更好):

      function getSomethingAsync (someState, callback) {
          req.invoke (function (data) {
             var result = processDataUsingState (data, someState);
             callback (result);
          });
      }
      

      【讨论】:

      • 哈维尔...为什么这样更好?投反对票的人...为什么不是一个好的答案?
      • 只是为了避免繁文缛节:一个函数调用和额外的名称只是为了在您可以在现场创建闭包时创建闭包。请记住,为函数命名只是将其分配给变量。
      • 但是给东西命名也提供语义和重用;虽然如果他的 makeCallback() 函数没有在其他任何地方使用,那么您的简化绝对有用,如果是这样,您将不得不复制代码。有时,将事物分解为命名函数有助于提高可读性。
      • 为函数命名可以让您在查看堆栈跟踪时更容易阅读
      猜你喜欢
      • 1970-01-01
      • 2016-09-03
      • 1970-01-01
      • 1970-01-01
      • 2016-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多