【问题标题】:Should I avoid creating this JavaScript closure?我应该避免创建这个 JavaScript 闭包吗?
【发布时间】:2012-12-05 11:47:12
【问题描述】:

这可能有点抽象,但我正在尝试了解 JavaScript 闭包等。请使用以下代码:

function MyObj() {
    var me = this;

    this.foo = function(bar) {
        // Do something with 'bar'
    }

    // Set up lots of local variables etc.
    // ....

    $(window).load(function() {
        // Add a delegated click handler to specific <input> elements
        $(document).on('click.myobj', 'input.special', function() {
            // Do something with the <input> that triggered the click event
            me.foo(this);
        });
    });
}

var myObj = new MyObj();

传递给它的匿名函数绑定到 click 事件创建一个引用 me 的闭包。我想知道的是这样做是否更好(以避免关闭):

$(window).load(function() {
    // Add a delegated click handler to specific <input> elements
    (function(localMe) {
        $(document).on('click.myobj', 'input.special', function() {
            // Do something with the <input> that triggered the click event
            localMe.foo(this);
        });
    })(me);
});

这是一种更好的方法,还是我对创建闭包过于偏执?或者,有没有“第三条路”?


编辑

另外,做这样的事情会更好吗:

$(window).load(function() {
    // Add a delegated click handler to specific <input> elements
    $(document).on('click.myobj', 'input.special', {localMe : me}, function(event) {
        // Do something with the <input> that triggered the click event
        event.data.localMe.foo(this);
    });
});

【问题讨论】:

  • 你想从中获得什么?
  • @Philipp,我的印象是创建 JavaScript 闭包可能会很昂贵,因为需要额外的数据来跟踪范围等。在这个例子中,我接受节省可能很少,但我想知道这种方法是否更好原则上

标签: javascript jquery closures performance


【解决方案1】:

后者(AFAIK)效率更高,但可能无法衡量,除非在紧密循环中使用。

原因是所有的变量解引用都必须遵循作用域链。在后一种情况下,变量localMe可以在匿名函数的参数列表中找到。

在前一种情况下,变量不是在那里找到的,而是在外部范围内。向上遍历作用域链需要额外的时间。

【讨论】:

    【解决方案2】:

    匿名函数现在在 javascript 中大量使用(作为参数和作用域/闭包的直接函数)。没有性能问题。

    但您可能会遇到代码阅读问题。因为当你看到一个变量时,你必须检查变量来自哪里。但这里没什么大不了的。

    在你的第二个例子中,你仍然有一个闭包“break”。因为在click 中的匿名函数中,您使用了localMe 变量。 localMe 是您的函数之外的函数的参数。

    // Here, 'me' is a direct local variable.
    $(window).load(function() {
        // Here, we are in an anonymous fonction, so 'me' is not a direct variable anymore. But you still can access it.
    
        // Add a delegated click handler to specific <input> elements
        (function(localMe) {
            // Here, 'localMe' is a direct local variable.
            $(document).on('click.myobj', 'input.special', function() {
                // We are in an anonymous function, so 'localMe' is not a direct variable anymore.
    
                // Do something with the <input> that triggered the click event
                localMe.foo(this);
            });
        })(me);
    });
    

    如果你真的想避免闭包“中断”,你应该将你的函数绑定到你的对象。但请注意,并非所有浏览器都支持函数上的bind 方法。

    【讨论】:

    • 不,localMe 是内部函数的参数,因此在最接近的范围内隐式可用。
    • 啊是的 - 我忘记了内部回调函数。我只是在看匿名函数。
    【解决方案3】:

    如果您从构造函数绑定事件,您将总是创建一个闭包。事实上,您甚至需要闭包来保留对您的实例的引用。但是,您可能会这样做:

    function MyObj() {
        this.foo = function(bar) {
            // Do something with 'bar'
        }
    
        // Set up lots of local variables etc.
        // ....
    }
    
    var myObj = new MyObj();
    $(function() {
        $(document).on('click.myobj', 'input.special', function() {
            myObj.foo(this);
        });
    });
    

    如果您只创建构造函数的单例实例,那无论如何都没关系。

    【讨论】:

      【解决方案4】:

      我可能会这样做:

      var bind = function( fn, me ) { return function() { return fn.apply(me, arguments); }; },
          Object = (function() {
              function Object() {
                  this.handler = bind(this.handler, this);
                  // Add a delegated click handler to specific <input> elements.
                  $(document).on("click.myobj", "input.special", this.handler);
              }
      
              Object.prototype.foo = function( bar ) {
                  // Do something with "bar".
              };
      
              Object.prototype.handler = function( event ) {
                  // Do something with the <input> that triggered the click even.
                  return this.foo(event.currentTarget);
              };
      
              return Object;
          })();
      
      var obj = new Object();
      

      这会跳过闭包和 iifes 的使用,而是使用 .apply。不确定它是否更有效,但这是另一种选择。

      【讨论】:

      • bind 不是事件处理程序的好选择,因为在那些 this 应该代表单击的对象,而不是外部绑定的变量。
      猜你喜欢
      • 2012-10-14
      • 1970-01-01
      • 2013-06-27
      • 2011-05-05
      • 1970-01-01
      • 1970-01-01
      • 2014-10-24
      • 2022-12-16
      • 2010-11-21
      相关资源
      最近更新 更多