【问题标题】:Where does an anonymous function gets its arguments匿名函数从哪里得到它的参数
【发布时间】:2014-02-06 08:23:58
【问题描述】:

我正在借助一本书学习开发 Windows 8 风格的应用程序。我正在阅读的这一章重点介绍用于开发的 HTML、CSS 和 JavaScript 语言。应用程序在 ListView 中显示您在“我的图片”文件夹中的图像,并在用户单击或点击图像时将其删除。下面是实现ListView中删除图片的代码:

var lv = document.getElementById('lv');
lv.addEventListener('iteminvoked', function (eventObj) {
    eventObj.detail.itemPromise.then(function (listViewItem) {
        var binding = files.dataSource.createListBinding();
        binding.fromIndex(listViewItem.index).then(function (dataItem) {
            var key = dataItem.key;
            files.dataSource.remove(key);
            binding.release();
         });
     });
});

我的问题是,addEventListener方法中匿名函数的eventObj参数在哪里取值?我在这里发现了一个类似的问题:Passing arguments in anonymous functions in JavaScript,但我无法完全理解。我在 MSDN 上搜索了 addEventListener 的文档,但它只是说它需要一个事件处理函数,但它没有说明参数。提前致谢。

【问题讨论】:

  • 除了关于 eventObj 来自何处的所有一般答案之外,您可能最感兴趣的是该对象本身的数据类型。这总是在事件的文档中找到;在你的情况下 WinJS.UI.ListView.oniteminvoked.

标签: javascript windows-8 microsoft-metro winjs windows-applications


【解决方案1】:

这相当简单:无论在内部调用该回调的任何函数都会传递参数。看,addEventListener 告诉正在执行的 Javascript 引擎在事件发生时调用您指定的回调函数。 javascript 引擎将您的匿名函数保存在某个变量中 - 稍后可以使用该确切变量调用它,传递任意数量的参数。

为了说明这一点,请考虑处理事件的内部函数(纯属虚构,只是为了说明它是如何完成的):

var callbacks = [];

function addEventListener(newEvent, newCallback) {
    callbacks.push({event : newEvent, callback : newCallback});
}

function handleEvent (someEvent) {
    for (var i = 0 ; i < callbacks.length ; i++ ) {
        if (callbacks[i].event == someEvent.name) {
            callbacks[i].callback(someEvent);
        }
    }
}

更多解释:

由于 javascript 是一种所谓的“函数式语言”,因此函数只是变量的值。

function someFunc () {}

实际上只是某种捷径(技术上不是,但它做同样的事情)

var someFunc = function () {}

话虽如此,当然可以将多个名称与一个函数相关联:

var someFunc  = function () {}
var sameFunc  = someFunc;
var stillSame = somefunc;
var alsoSame  = stillSame;

您可以使用任何这些名称调用该函数,当然包括传递参数:

var someFunc  = function (arg) { alert(arg); }
var sameFunc  = someFunc;
sameFunc("It worx");

您甚至可以在不命名的情况下调用函数:

(function () {alert("test")})();<

(function (arg) { alert(arg); })("test")

使用这个概念来变态最终导致(但是还有很长的路要走)y-combinator 之类的东西。

【讨论】:

    【解决方案2】:

    事件处理程序可以附加到各种对象,包括 DOM 元素、文档、窗口对象等。当事件发生时, 创建事件对象并按顺序传递给事件 听众。

    来源:https://developer.mozilla.org/en-US/docs/Web/API/Event

    事件监听器事件处理程序可以是匿名函数或命名函数,这并不重要。关键是 事件接口 定义了传递给处理程序的事件对象。

    要从您正在使用的事件中准确找出事件属性,请参阅 windows 文档:http://msdn.microsoft.com/en-us/library/windows/apps/br211827.aspx

    【讨论】:

    • 这也没有解释事件最终是如何在 OP 创建的无名函数中结束的。
    【解决方案3】:

    事件侦听器接收的参数从dispatchEvent 发送,即当事件调度时,它会将事件对象传递给您的处理程序。

    请参阅此documentation,了解如何创建和调度事件。事件对象可以在结构上有所不同,以将信息传递给事件处理程序以执行必要的步骤。因此,在您的情况下,当您执行 lv.dispatchEvent(newevent) 时,这会将 newevent 作为 eventObj 发送到您的事件处理程序。

    请记住,可以有多个事件处理程序侦听一个事件,因此浏览器会为事件侦听器维护一个堆栈,以便按顺序运行它们,每个事件侦听器都传递了eventObj

    匿名函数与命名函数没有区别。在 JavaScript 中,函数是一等对象,即普通对象。因此,您可以像常规对象(数字、字符串)一样传递它们,而无需命名它们。唯一的问题是重用成为问题。

    【讨论】:

      【解决方案4】:

      要理解这段代码,你需要重写一下:

      var lv = document.getElementById('lv'),
          invokeHandler = function (eventObj) {
              var promiseFullfilled = function (listViewItem) {
                      var binding = files.dataSource.createListBinding(),
                          anotherPromiseFullfilled = function (dataItem) {
                              var key = dataItem.key;
                              files.dataSource.remove(key);
                              binding.release();
                          };
      
                      binding.fromIndex(listViewItem.index).then(anotherPromiseFullfilled);
                  };
      
              eventObj.detail.itemPromise.then(promiseFullfilled);
          };
      
          lv.addEventListener('iteminvoked', invokeHandler);
      

      这段代码的工作原理是一样的,但是现在很明显addEventListenerthen 实际上并不了解它们所传递的回调函数任何。但是,他们可以使用 Function.prototype.callFunction.prototype.apply 来应用参数:

      // This is PSEUDOCODE, event model actually works in a totally different way
      
      HTMLElement.prototype.addEventListener = function(eventType, callback, bubbles) {
          // callbacks is some internal collection for this specific element, probably available via a closure, looks something like:
          // { 
          //   'someEventType': [callback1, callback2], 
          //   'someOtherEvent': [callback1, callback3, callback4] 
          // }
          callbacks[eventType].push(callback); 
      }
      
      // This is called whenever an event is triggered on an element
      HTMLElement.prototype.dispatchEvent = function(event) {
          callbacks[event.type].forEach( function(callback) {
              return callback.call(this, event); // the callback is called with 'this' set to the element DOM object, and 'event' is the first argument  
          });
          // then it can bubble or cancel depending on the event type and callback results
      }
      

      【讨论】:

        【解决方案5】:

        它是一个CustomEvent,所有的过程都是这样的:

        //you add a anonymous function to a specific listener
        lv.addEventListener('iteminvoked', function (eventObj) {
            console.log(eventObj===myEvent);
        });
        
        //somewhere in your code a CustomEvent gets created based on "iteminvoked" key
        var myEvent = new CustomEvent("iteminvoked", {
            itemInfo: {
                name: "yourItem"
            },
            bubbles: true,
            cancelable: false
        });
        
        //somewhere when an item gets invoked this code raise the `iteminvoked` trigger
        lv.dispatchEvent(myEvent);
        

        作为侦听器传递的所有函数都基于键存储,例如:

        var observers = {
            "iteminvoked" : [f1, f2],
            //other keys
        }
        

        它与没有名称没有任何关系,函数对象存储在某种数组中。 dispatchEvent 穿过数组并调用所有函数,并将 myEvent 作为参数传递。这是一个Observer 模式,用 javascript 实现,我在自己的 javascript 库中实现过一次,例如:

        var lv = /*your element*/;
        if(observers["iteminvoked"]){
            for(var i=0;i<observables["iteminvoked"].length;i++){
                var func = observables["iteminvoked"][i];
                var o = func.call(lv, myEvent);
        
                //this line is to support return false
                if(o!==undefined && o===false) break;
            }
        }
        

        如您所见,调用所有观察者是dispatchEvent 责任,无论它是否具有名称,您的函数都会以lv 作为this 上下文和myEvent 作为参数来调用。

        【讨论】:

        • 这并没有解释该参数是如何传递给“无名”的函数的。
        • @JohannesH。实际上确实如此,但这可能发生在 WinJS.UI 的幕后,而不是在用户的代码中。
        • 是的,确实如此。不过,这个概念可以在 javascript 本身中重建。
        • 虽然我很欣赏你在答案上的努力,但我必须承认我不太明白你在说什么,主要是因为这是我第一次尝试像 JavaScript 这样的动态语言,所以像 observables 这样的概念,事件和东西对我来说是新的。仍然再次感谢!
        猜你喜欢
        • 2017-07-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-04
        • 1970-01-01
        • 1970-01-01
        • 2012-07-04
        相关资源
        最近更新 更多