将特定上下文绑定到事件处理程序:了解功能上下文,返回语句和闭包?

时间:2013-09-17 21:10:21

标签: javascript functional-programming closures anonymous-function

我正在阅读John Resig的“Javascript Ninja的秘密”,并在其中讨论了如何更改事件处理程序的上下文。在其中他有以下代码。我不知道为什么它有效。 非常迷失于此。

<body>
    <button id="test">Click Me!</button>

    <script>
      function bind(context,name){                                 //#1
        return function(){                                         //#1
          return context[name].apply(context,arguments);           //#1
        };                                                         //#1
      }                                                            //#1

      var button = {
        clicked: false,
        click: function(){
          this.clicked = true;
          alert('It has been clicked! The value of clicked is ' + clicked);
        }
      };

      var elem = document.getElementById("test");
      elem.addEventListener("click",bind(button,"click"),false);     //#2

    </script>

  </body>

即我不理解绑定函数本身,我不明白为什么需要在匿名函数中使用它。因为如果我删除bind()函数并尝试以下任何一种,我总是会收到错误消息'clicked is undefined'。对我来说,他们中的任何一个都可以通过将'this'参数分配给按钮对象来工作,从而可以访问其被点击的属性:

    elem.addEventListener("click",button.click.apply(button),false);

在下一个案例中,我正在讨论javascript的原生'绑定'功能

    elem.addEventListener("click",button.click.bind(button),false); 

据我所知,下一个陈述将是John Resig的bind()函数返回

    elem.addEventListener("click",function() { button.click.apply(button, arguments)},false); 

但等等,John Resig的绑定函数中有两个返回语句。在这一点上,我只是猜测 - 下一个怎么样?

    elem.addEventListener("click",function() { return button.click.apply(button, arguments)},false); 

以上陈述都不起作用。

其次,为什么bind函数有两个return语句?在我看来,bind的工作原理如下:

function bind(context,name){ 
            return function(){ context[name].apply(context,arguments); }; 
          }

第三,如果我采用John Resig的绑定函数并尝试将其赋值给变量,比如变量ninja,就像这样:

var ninja = bind(button,"click");
ninja;

我希望看到函数返回给我看起来像:

function(){ button["click"].apply(button,arguments);

因为上下文和名称的值将通过闭包填充。但它看起来像:

function(){ context[name].apply(context,arguments);

所以......我希望我对所有这些问题产生混淆的根本原因源于我所缺少的基本概念。想法?

3 个答案:

答案 0 :(得分:3)

这是很多问题!我会尽力帮你澄清一下。

  • addEventListener期望函数引用作为第二个参数。当您传递button.click.apply(button)时,您立即calling click,并将点击函数(示例中为undefined)的返回值传递给addEventListener。这就是为什么它不起作用。

  • 另一方面,
  • bind返回一个函数,因此调用它来获取事件监听器函数是合适的。

  • 使用native bind的示例以及后续版本的示例应该会尝试模拟原生bind

  

如果我删除bind()函数并尝试任何操作,我总是会收到错误消息“clicked is undefined”。

这是因为您的button.click功能存在缺陷。它访问一个无处定义的变量clicked。只有在使用this的全局对象(如button.click.call(window))无意中调用函数时,this.clicked = true才会创建一个可以在以后访问的全局变量。将功能更改为

    alert('It has been clicked! The value of clicked is ' + this.clicked);
//                                                          ^^^^^

你的每一种方法都会奏效。

  

但等等,John Resig的绑定函数中有两个返回语句。

Resig示例中的内部return将是事件侦听器的最终返回。因此,例如,如果要阻止单击处理程序的默认值,可以将其设为return false。如果没有内部return,事件侦听器将返回undefined,而不是转发代理侦听器函数返回的内容。这不是您的示例所必需的,但当bind用于partial application时,它是button["click"]函数的一个重要特征。

  

我希望看到返回给我的函数看起来像

最后,关于你的闭包混淆:闭包是一个函数,它保持对来自外部作用域的东西的引用(基本上所有函数都在js中执行)。 引用不会像您在bind示例中所期望的那样替换函数体代码。在Resigs context[name]返回的函数中,button["click"]表示context,但代码实际上并未替换; button指向name"click"包含字符串{{1}}。

答案 1 :(得分:1)

我希望这能解释你的两个问题。

绑定功能

function bind(context,name){
    return function(){
      return context[name].apply(context,arguments);
    };
}

会在触发事件时解决,它将返回button.name(event)。处理方式是通过

当事件被触发时,addEventListener将使用fn.apply(elm, arguments)为已注册的每个侦听器执行代码。扩展这个:

((function(){return button['click'].apply(button, arguments);})).apply(elm, arguments)

匿名函数用于传递已触发事件的原始参数,并将elm替换为button作为this代码中的button.click引用。

其他代码行会导致错误,因为在apply上使用function函数时,无法指定/引用参数。另一种思考方式是,在运行时,您正在创建由elm定义的function临时方法。 function可能已定义参数,但使用在apply之前链接的那些参数将在apply之前使用参数的当前值/参考执行函数。

答案 2 :(得分:1)

基本上它就像这样。

  • 上下文是您要调用的对象。
  • 名称是您要调用的对象中的函数。

上下文[名称]。适用(上下文中,参数)

装置 返回函数

function(){
 context.name(arguments)
}

当你想要触发context.name(arguments)时,它会触发,而不是在页面加载时立即触发。