【问题标题】:Clarification on closures in Javascript with AJAX使用 AJAX 澄清 Javascript 中的闭包
【发布时间】:2011-12-18 05:58:11
【问题描述】:

我应该已经正确理解了 Javascript 中的 Cloures,但我显然没有......

目前,我正在阅读的文本具有抽象 AJAX 调用的功能:

function request(url, callback){
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = (function(myxhr){
    return function(){
      callback(myxhr);
    }
  })(xhr);
  xhr.open('GET', url, true);
  xhr.send('');
}

这是我的实际问题:我的大脑拒绝理解为什么这个不起作用:

function request(url, callback){
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = callback(xhr);
  xhr.open('GET', url, true);
  xhr.send('');
}

我的意思是,以“我的”方式,我想象会发生的是我调用 say request('http://...', a_callback)。在 request() 中,创建了一个新的 xhr 对象,并将其分配给回调......它不会工作吗? (讨厌的)副作用是什么?根据我的(有限)理解,您需要闭包,例如在循环中您可能最终引用函数变量的最新值。但是这里...“var xhr=...”不是确保每次都创建一个新对象吗?

请解释为我的智商为 30(这可能是真的:D)

佣兵。

【问题讨论】:

  • 请解释为好像我的智商为 30 - 你的问题听起来更像是“AJAX,你没有工作,请帮助,紧急!!!!!!1111”。
  • :-) 好吧,当我写这个问题时,我什至把“我的”例子弄错了,所以我想我用英语写作可以获得额外的分数,但仅此而已!

标签: javascript ajax closures


【解决方案1】:

当您编写xhr.onreadystatechange = callback(xhr) 时,您会立即调用callback 并将其结果分配给onreadystatechange。

【讨论】:

    【解决方案2】:

    在您显示的代码中,您实际上是在 调用 callback,因为末尾有 ()。

    【讨论】:

      【解决方案3】:

      在第一个示例中,您不需要额外的闭包。这很好用:

      function request(url, callback){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
            callback(xhr);
        };
        xhr.open('GET', url, true);
        xhr.send('');
      }
      

      在现代浏览器(或浏览器patched with a polyfill)中,您也可以这样做:

      function request(url, callback){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = callback.bind(null, xhr);
        xhr.open('GET', url, true);
        xhr.send('');
      }
      

      edit — 还要注意@Raynos 提供的答案。您实际上不需要将 XHR 对象作为参数传递。

      再次编辑——在回复一个合法的评论时,你问为什么你最初的想法行不通:

      function request(url, callback){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = callback(xhr);
        xhr.open('GET', url, true);
        xhr.send('');
      }
      

      问题出在这行:

        xhr.onreadystatechange = callback(xhr);
      

      这会将值 返回 从 调用 分配给当时和那里的回调函数。换句话说,不是在状态改变时建立要调用的东西,而是立即进行调用。这个错误很常见,因为很容易读错。然而,任何时候,JavaScript 都会看到一个函数引用后跟一个带括号的参数列表,这被解释为执行函数调用的请求,而不是对未来函数调用的函数包装器的请求。

      【讨论】:

      • 所以我可以写这个,对吧?函数请求(网址,回调){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = 回调; xhr.open('GET', url, true); xhr.send(''); }
      • 是的,你也可以这样做!唯一的问题是浏览器不会将 XHR 对象作为参数传递,但它会为您将其设置为 this 值。
      • Hey Pointy... 事实证明我是对的!顺便说一句,我从 Stoyan Stefanov 的“面向对象的 Javascript”中得到了带有闭包的示例。一本很棒的书,但是第 258 页上的示例,xhr 在闭包中,真的让我很震惊……我想“好吧,我不会在这里使用闭包,因此他这样做了我会被塞满...但是为什么要关闭?!?”。我用头撞了大约 2 个小时,试图弄清楚为什么你可能需要关闭......结果你不需要:D
      • 投反对票,因为它实际上并没有解释为什么它在他的示例中不起作用。
      • @MJB 解决了,我想。
      【解决方案4】:

      好问题。函数调用和指向函数的指针之间有一个重要的区别——你想给他们一个指向函数的指针(不是真的,它是一个闭包,但这就是我所说的),以便他们以后可以调用它.如果你曾经做过函数式编程,或者玩过 Lisp 家族中的任何东西,这一点非常重要。

      【讨论】:

        【解决方案5】:

        您无需将xhr 传递给回调。

        你可以xhr.onreadystatechange = callback;

        然后使用this 在回调中引用xhr 对象。

        示例:

        xhr.onreadystatechange = function () {
          console.log(this.readystate);
        };
        

        免责声明:跨浏览器支持的一些价值>_>

        【讨论】:

        • 呃...this?啊对;因为浏览器以 XHR 对象作为接收者调用 XHR 回调。 (对吧?)
        • @Pointy 因为invoking an event listener 就是这样工作的。见 4.6。 XHR 是一个 EventTarget,target 是 XHR 对象。
        【解决方案6】:

        在 C 和大多数其他常用语言中,函数返回后,所有局部变量都不再可访问,因为堆栈帧已被破坏。

        在 JavaScript 中,如果你在另一个函数中声明一个函数,那么局部变量在从你调用的函数返回后仍然可以访问。

        【讨论】:

          【解决方案7】:

          我正在回答我自己的问题,因为编辑会混淆一切 - 如果这不是这样做的方法,请原谅。

          所以...在我的示例中,我实际上想要写的是:

          function request(url, callback){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function(){ callback(xhr); }
            xhr.open('GET', url, true);
            xhr.send('');
          }
          

          或者,因为我不需要 xhr(我可以使用“this”):

          function request(url, callback){
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = callback;
            xhr.open('GET', url, true);
            xhr.send('');
          }
          

          所以,最后,我是对的……使用闭包(如原始问题所示)完全是浪费时间,对吧?

          如果有一个循环会很重要,我必须设置几个回调。但是在这种情况下,由于每次调用“request”时都会创建一个新的 xhr,并且设置了正确的回调,因此实际上不需要闭包.. 对吗?

          佣兵。

          【讨论】:

            猜你喜欢
            • 2012-11-09
            • 1970-01-01
            • 2015-03-29
            • 1970-01-01
            • 2019-01-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-11-09
            相关资源
            最近更新 更多