1

我正在阅读 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() 函数并尝试以下任何操作,我总是会收到错误消息“点击未定义”。对我来说,它们中的任何一个都可以通过将“this”参数分配给按钮对象来工作,从而可以访问其 clicked 属性:

    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);

因为 context 和 name 的值将通过闭包填充。但它看起来像:

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

所以......我希望我对所有这些问题感到困惑的根本原因源于我所缺少的相同基本概念。想法?

4

3 回答 3

3

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

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

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

  • 您使用nativebind的示例以及其后的示例应该可以工作,该示例尝试模拟 native bind

如果我删除 bind() 函数并尝试任何方法,我总是会收到错误消息“点击未定义”。

那是因为你的button.click功能有缺陷。clicked它访问一个在任何地方都没有定义的变量。只有在无意中使用this(like 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. 如果没有 inner return,事件侦听器将返回undefined而不是转发代理侦听器函数返回的内容。这对于您的示例不是必需的,但是bind当它用于部分应用程序时,它是该函数的一个有价值的特征。

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

最后,关于你的闭包混淆:闭包是一个函数,它保留对外部范围的东西的引用(基本上所有函数都在 js 中这样做)。但是引用不会像您在示例中所期望的那样将函数体代码替换为button["click"]. 在 Resigs 返回的函数中bindcontext[name]表示button["click"],但实际上并未替换代码;context指向buttonname持有字符串"click"

于 2013-09-17T21:24:04.830 回答
1

基本上它是这样工作的。

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

上下文[名称].apply(上下文,参数)

表示返回函数

function(){
 context.name(arguments)
}

它会在您希望它触发时触发 context.name(arguments),而不是在页面加载时立即触发。

于 2013-09-18T05:14:52.033 回答
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在 a 上使用函数时function,无法指定/引用参数。另一种思考方式是,在运行时,您正在创建一个elmfunction. function可能定义了参数,但使用在 之前链接的参数将使用参数的当前值/引用apply执行之前的函数。apply

于 2013-09-17T23:32:19.987 回答