词汇范围,可变生命周期和javascript中的承诺

时间:2015-11-26 14:17:20

标签: javascript scope promise

我已经阅读了SO中的答案数量,但是我无法在脑海中找到关于javascript中可变生命时间的明确解释。通常答案是约hoisting/shadowing,这不是imo的情况。例如,我们经常将这些类型的脚本与jQuery一起编写;

function getSomeData() {
  var $container = $('#someContainer');
  $.get('/url', function(data) {
    $container.html(data);
  });
}

现在,我的主要问题是$container变量如何保持匿名回调函数的可用性?我知道,当执行到$.get时,get会立即返回,getSomeData最终会返回,但get可能会在将来的任何时间返回。因此,我不得不认为getSomeData实际上仍然存在于函数调用堆栈中,以便为回调函数提供$container变量,因为据我所知,如果在范围内找不到变量,则使用它,解释器在父范围内查找(到全局范围)。以下是附带问题:

  • 这种模式会导致性能下降吗?
  • 如果我将匿名函数更改为声明的函数并使用like;

    var callback = function(data) { $container.html(data); } $.get('/url', callback);

    这种用法有什么优点(除了调试目的和可读性之外)?

  • 如果没有涉及此处的函数调用堆栈,那么在承诺结算之前,$container存储在哪里?请注意,这种嵌套也是可能的:

    function getSomeData() { var $container = $('#someContainer'); var replaceHtml = function(data) { $container.html(data); } $.get('/url', function(data) { replaceHtml(data); }); }

请对此作出明确解释,谢谢!

1 个答案:

答案 0 :(得分:2)

  

现在,我的主要问题是$ container变量如何用于匿名回调函数?

     

...因此我不得不认为getSomeData实际上仍然保留在函数调用堆栈中,以便为回调函数提供$ container变量,因为据我所知,如果在范围内找不到变量,那么used,interpreter在父范围内查找(到全局范围)。

你真的很接近理解这一点。只需进行一些更正和澄清,您就可以获得。

它与函数调用堆栈无关。在回调运行之前getSomeData返回你是对的。这意味着它就像正常一样从堆栈中出来。但是,它的局部变量等 生活在,即使该函数已返回。为什么?因为回调有间接引用它们。

调用JavaScript函数时,会创建一个(理论上)对象,该对象是该调用的执行上下文。该上下文包含各种内容,包括一个名为变量环境的对象,其中包含与调用相关的参数和变量。在上下文中创建的任何函数都保持对该上下文的可变环境对象的引用,因此即使该函数已返回,也可以访问它们。这些函数称为闭包,因为它们"关闭"它们的创建环境。

因此,当回调引用$container变量时,JavaScript引擎查看回调自身的上下文及其变量环境,而不是在那里找到$container,查看下一个包含环境。它在那里找到$container并使用它。

让我们把它分解成几步。请注意,此处有挥手示意,the spec包含详细信息。

  1. 调用getSomeData
  2. 引擎推送堆栈上的返回地址
  3. 引擎为呼叫
  4. 创建执行上下文
  5. 引擎为上下文创建变量环境,并使用参数,局部变量等填充它。
  6. 引擎运行函数中的代码
    • 部分原因是创建了回调函数,该函数提供了对变量环境对象的引用
    • 另一部分是将该功能参考传递给$.get,这会让它保持一段时间
  7. 代码exectuion"脱落"函数结束,因此引擎从堆栈中弹出返回地址并继续
  8. 一段时间后,$.get通过对其保留
  9. 的引用来调用回调
  10. 为调用回调
  11. 创建了新的上下文和变量环境
  12. 回调代码运行,在自己的变量环境中查找内容以及创建它的环境(对于$container
  13. 代码执行"脱落"在函数结束时,引擎弹出堆栈,$.get删除对函数的引用
  14. 由于该功能没有任何引用它,它有资格进行垃圾收集
  15. 在某些时候,引擎的GC算法会处理该函数,因此该函数不再从调用getSomeData引用变量环境对象,因此该对象符合GC的条件
  16. 在某些时候,引擎处理变量环境对象
  17. 现在,除了副作用(比如实际使用$.get中的信息),所有内容都已清理
  18.   

    这种模式会导致性能下降吗?

    不在其中,不是。 如果您创建了一个函数并将其保留在,那么由于它保留了创建它的变量环境,这可能会导致内存影响。但是一旦你释放了这个函数(在这种情况下,一旦$.get调用它并删除它的引用),那么函数可以是GC,并且变量环境(及其内容)可以是GC' d

    在上面我讲了很多对象和垃圾收集,但当然JavaScript引擎可以并且围绕这个做很多优化。它是现代发动机比旧发动机快得多的重要原因。 (只是一部分,但部分。)

      

    如果我将匿名函数更改为声明的函数并使用like;

    var callback = function(data) {
      $container.html(data);
    }
    $.get('/url', callback);
    
         

    这种用法是否有任何优点(除了调试目的和可读性之外)?

    没有

      

    如果这里没有涉及函数调用堆栈,那么在容器结算之前,$ container存储在哪里?

    见上文。