最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Infinite loop when using jQuery to trigger event on object with function of same name - Stack Overflow

programmeradmin3浏览0评论

That title probably doesn't help much, I tried though. Anyway I ran into this extremely mysterious (and frustrating) bug resulting in a RangeError: Maximum call stack size exceeded in some OO JS I'd written. Took me a couple hours but I finally got to the cause. Here's a simple example that will result in the same exception:

// Class
var Foo = function(){
    // "Public" function
    this.bar = function(){
        console.log('loop!');
        $(this).trigger('bar');
    }
}

var foo = new Foo();
$(foo).trigger('bar');

Running this code will result in loop! being logged to the console a ton of times before eventually resulting in the range exception.

Clearly there's something about jQuery's trigger function I don't understand, and it boils down to this: why would foo's bar function be called at all? Yes, the event name and the class's function name are the same, but I don't understand how the two are related.

That title probably doesn't help much, I tried though. Anyway I ran into this extremely mysterious (and frustrating) bug resulting in a RangeError: Maximum call stack size exceeded in some OO JS I'd written. Took me a couple hours but I finally got to the cause. Here's a simple example that will result in the same exception:

// Class
var Foo = function(){
    // "Public" function
    this.bar = function(){
        console.log('loop!');
        $(this).trigger('bar');
    }
}

var foo = new Foo();
$(foo).trigger('bar');

Running this code will result in loop! being logged to the console a ton of times before eventually resulting in the range exception.

Clearly there's something about jQuery's trigger function I don't understand, and it boils down to this: why would foo's bar function be called at all? Yes, the event name and the class's function name are the same, but I don't understand how the two are related.

Share Improve this question asked Dec 11, 2013 at 18:09 MadbreaksMadbreaks 19.5k7 gold badges61 silver badges74 bronze badges 4
  • 1 Sorry, but I cant see the problem. You first call bar from foo.. and inside bar, you call again bar from foo (this). So it is an expected behavior. – wendelbsilva Commented Dec 11, 2013 at 18:16
  • 1 ...resulting in a loop... – Andy Commented Dec 11, 2013 at 18:17
  • Interesting...it seems that the methods of a constructor function act as events, and can be triggered using jQuery. Edit: Doesn't have to be a constructed object. Any regular object can have any of its methods triggered – tewathia Commented Dec 11, 2013 at 18:23
  • @wendelbsilva So, you're saying that $(foo).trigger('bar') is equivalent to foo.bar()? That's not true in my experience, the former triggers an event (not "calls a function") on the target object. – Madbreaks Commented Dec 11, 2013 at 18:27
Add a ment  | 

4 Answers 4

Reset to default 8

You would need to use .triggerHandler to mitigate this issue.

Per the jQuery docs:

Note: For both plain objects and DOM objects other than window, if a triggered event name matches the name of a property on the object, jQuery will attempt to invoke the property as a method if no event handler calls event.preventDefault(). If this behavior is not desired, use .triggerHandler() instead.

http://jsfiddle/bcsqF/

From the jQuery Trigger documentation:

Description: Execute all handlers and behaviors attached to the matched elements for the given event type.

bar is a propery or behavior of Foo, so the bar property (which is a method) is called on $(foo).trigger('bar');. In the bar function, the this refers to the foo class, so the $(this).trigger('bar');. calls the bar function on foo. This would be qual to calling this.bar() in the bar function, which is a more obvious recursion.

All in all this isn't´the behaviour I would have expected to, I expected that Trigger could be only called on DOM elements, but the documentation states otherwise.

Lets dig into jQuery source.

trigger:

function (type, data) {
    return this.each(function () {
        jQuery.event.trigger(type, data, this);
    });
}

jQuery.event

function (src, props) {

    // Allow instantiation without the 'new' keyword
    if (! (this instanceof jQuery.Event)) {
        return new jQuery.Event(src, props);
    }

    ...

    // Put explicitly provided properties onto the event object
    if (props) {
        jQuery.extend(this, props);
    }

    ...
}

As you can see jQuery.event object is constructed in a way that it's extended with own properties of you object. It means that you can use trigger to invoke them. Hence a loop.

In addition to calling any attached event handlers, trigger will also attempt to invoke the native behavior. So if you are calling $('form').trigger('submit') in addition to executing any bound handlers, it will fire the native method form.submit to actually send the form. I suppose trigger looks on the object for a method with the same name as the triggered event, and assumes that to be acpanying native event, which it then calls.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论