为什么这个代码块会创建一个无限循环?

时间:2012-05-22 15:49:10

标签: javascript jquery

今天早些时候我正在玩弄小提琴,试图回答一个问题并发现一个令人困惑的事情。作为一个JS新手,我无法调试自己出错了什么。我甚至试图在jQuery源代码中检查$.fn.show的source0但是无法弄清楚什么是错误的。

HTML:

<input type='text' id='dataBox'/>
<input type='button' value='toggle' id='toggleButton' />​

jQuery代码:

jQuery(function ($) {
    var _oldShow = $.fn.show;
    $.fn.show = function (speed, oldCallback) {
        return $(this).each(function () {
            var obj = $(this),
                newCallback = function () {

                    if ($.isFunction(oldCallback)) {
                        oldCallback.apply(obj);
                    }
                    obj.trigger('afterShow');
                };
            obj.trigger('beforeShow');
            if(speed)    
                _oldShow.apply(obj, [speed,newCallback]);
            else    
                _oldShow.apply(obj, [newCallback]);
        });
    }
});


$('#dataBox').bind('beforeShow', function () {
        alert('beforeShow');
    });


$('#toggleButton').click(function(){
        $('#dataBox').show();
}); 

问题在于我做了一些错误,导致这一行无限次执行      obj.trigger( 'beforeShow');

因此此块中的alert

 $('#dataBox').bind('beforeShow', function () {
    alert('beforeShow');
 });  

似乎没有停止。

不知道我想做什么,或者如果可以通过其他任何方式做到这一点,有人可以解释我在这里做错了什么。我已经尝试了几个小时,但无法弄明白。

FIDDLE

3 个答案:

答案 0 :(得分:2)

让我们来看看这一节

    if(speed)    
        _oldShow.apply(obj, [speed,newCallback]);
    else    
        _oldShow.apply(obj, [newCallback]);

    });

_oldShow之前被$.fn.show;指定为.apply()this调用带参数的函数作为数组,并能够设置{{1}}的上下文。 (see here)

因此,在函数结束时,我们总是最终再次调用函数,在beforeShow之前无限触发。

答案 1 :(得分:1)

上面代码的问题在于,jQuery在某些时候从内部调用$.fn.show并创建无限循环。因此,防止这种情况的正确方法是进行一些参数检查,如下所示:

 jQuery(function($) {
    var _oldShow = $.fn.show;
    $.fn.show = function(speed, easing, oldCallback) {

        var args = Array.prototype.slice.call(arguments),
            duration = args[0] || 0,
            easing = 'linear',
            callback = function() {},
            callbackArgIndex = 1;

        // jQuery recursively calls show sometimes; we shouldn't
        //  handle such situations. Pass it to original show method.    
        if (!this.selector) {
            _oldShow.apply(this, args);
            return this;
        }

        if (args.length === 2) {
            if ($.isFunction(args[1])) {
                callback = args[1];
                callbackArgIndex = 1;
            }
            else {
                easing = args[1];
            }
        }
        else if (args.length === 3) {
            easing = args[1];
            callback = args[2];
            callbackArgIndex = 2;
        }

        return this.each(function() {
            var obj = $(this),
                oldCallback = callback,
                newCallback = function() {

                    if ($.isFunction(oldCallback)) {
                        oldCallback.apply(obj);
                    }
                    obj.trigger('afterShow');
                };
            obj.trigger('beforeShow');
            args[0] = duration;

            if (callback) {
                args[callbackArgIndex] = newCallback;
            }
            else {
                args.push(callback);
            }

            _oldShow.apply(obj, args);


        });
    }
});


$('#dataBox').bind('beforeShow afterShow', function(e) {
    alert(e.type);
});


$('#toggleButton').click(function() {

    $('#dataBox').show();

});​

这样可以正常工作并且事件被正确解雇。

阻止无限循环的块是

if (!this.selector) {
      _oldShow.apply(this, args);
      return this;
}

这是做什么的,调用原始函数并在jQuery多次调用$.fn.show的情况下返回(jQuery似乎出于某种原因这样做)。

Working Fiddle

答案 2 :(得分:1)

查看show function code(在警报中): http://jsfiddle.net/D9vP6/4/

为了规范论证,似乎在某种条件下打电话给自己。重新定义此函数后,您将收到无限递归。

为避免此类行为,您应该在代码中进行所有这些规范化,并且在某些情况下不要触发事件。