Javascript 第0行的堆栈溢出

Javascript 第0行的堆栈溢出,javascript,jquery,internet-explorer,stack-overflow,Javascript,Jquery,Internet Explorer,Stack Overflow,我有一个表单验证脚本,不幸的是,它在崩溃(IE7)前不久返回了第0行的堆栈溢出警告框,在IE8中直接崩溃(它首先工作,非常缓慢) 我为您的测试制作了一个JSFIDLE:在您向需要验证的输入中输入一个值之后,堆栈溢出就会发生,然后失去它的焦点。 (email字段是ajax驱动的,因此不起作用) 相关Javascript: jQuery(document).ready(function($) { var inputs = $('input[data-validation-method]')

我有一个表单验证脚本,不幸的是,它在崩溃(IE7)前不久返回了第0行的
堆栈溢出警告框,在IE8中直接崩溃(它首先工作,非常缓慢)

我为您的测试制作了一个JSFIDLE:在您向需要验证的输入中输入一个值之后,堆栈溢出就会发生,然后失去它的焦点。 (email字段是ajax驱动的,因此不起作用)

相关Javascript:

jQuery(document).ready(function($) {

    var inputs = $('input[data-validation-method]');
    var fields = $();
    var classes = ['fail', 'win'];

    //Methods of validation, must return an object like so {result: [boolean], message: [string or false]} as a parameter of the callback() function;
    var methods = {

        'email' : function(field, dependancies, callback) {
            var value = field.val();
            var response = false;
            field.addClass("loading");
            $.post(
               ajaxData.url, 
               {
                  'action':'validate_form',
                  'value': value,
                  'method': field.data('method')
               }, 
               function(response){
                   return callback(response);
               }
            ).complete(function() {
                field.removeClass("loading");
            });
        },

        'password' : function(field, dependancies, callback) {
            var value = field.val();
            var response = {};
            if (value.length < 8) {
                response.result = false;
                response.message = 'Your password must be a minimum of 8 characters';
            } else {
                response.result = true;
                response.message = false;
            }
            return callback(response);
        },

        'verify_password' : function(field, dependancies, callback) {
            var value = field.val();
            var response = {};
            if (value != dependancies["password"].val()) {
                if (!dependancies["password"].val() || !value) {
                    return false;
                }
                response.result = false;
                response.message = 'Passwords do no match';
            } else {
                response.result = true;
                response.message = false;
            }
            return callback(response);
        }
    }

    // Prepare fields for validation
    inputs.each(function() {
        createField($(this));
    });

    function createField (field) {
        inputs = inputs.not(field);
        var method = field.attr('data-validation-method');
        var requires = field.attr('data-validation-requires');
        if (!!requires) {
            requires = requires.split(',');
            var dependancies = {};
            $.each(requires, function(key, value) {
                var element = $('#' + value);
                if(element.length) {
                    dependancies[element.attr('id')] = element;
                    if(inputs.find(element).length) {
                        createField(element);
                    }
                    if ($.isArray(element.data('linked_fields'))) {
                        element.data('linked_fields').push(field);
                    } else {
                        element.data('linked_fields', [field]);
                    }
                }
            });
        }
        if (methods[method]) {
            fields = fields.add('#' + field.attr('id'));
            field.data('method', method);
            field.data('dependancies', dependancies);
        }
    }

    function validate (field) {
        var callback = function(response) {
            field.data('response', response);
            if (response) {
                toggleFlag(field, 'show');
            } else {
                toggleFlag(field, 'remove');
            }
            if($.isArray(field.data('linked_fields'))) {
                $.each(field.data('linked_fields'), function(key, value) {
                    validate(value);
                });
            }
        }
        methods[field.data('method')](field, field.data('dependancies'), callback);
    }

    fields.focus(function() {
        var field = $(this);
        field.data("value", field.val());
        field.bind("propertychange keyup input paste", function(event){
            if(field.data("response") && (field.val() != field.data("value"))) {
                toggleFlag(field, "hide");
                if($.isArray(field.data('linked_fields'))) {
                    $.each(field.data('linked_fields'), function(key, value) {
                        toggleFlag(value, "hide");
                    });
                }
            }
        });
    });

    fields.blur(function() {
        var field = $(this);
        if (field.val().length) {
            if (field.val() != field.data("value")) {
                toggleFlag(field, "remove");
                validate(field);
            } else {
                toggleFlag(field, "show");
            }
        } else {
            toggleFlag(field, "remove");
        }
    });

    function toggleFlag (field, method) {
        var flag = field.data("flag");
        var response = field.data("response");
        if (response) {
            switch (method) {
                case "show":
                    if (response.message) {
                        if(!flag) {
                            flag = $('<span class="pie ' + classes[~~response.result] + '">' + response.message + '</span>').insertAfter(field);
                            field.data("flag", flag);
                            flag.hide();
                        }
                        if (!flag.data("active")) {
                            flag.data("active", true);
                            flag.stop(true, true).animate({height: "show", opacity: "show"}, 500);
                        }
                    }
                    field.addClass(classes[~~response.result]);
                    break;
                case "hide":
                    if (flag) {
                        if (flag.data("active")) {
                            flag.data("active", false);
                            flag.stop(true, true).animate({height: "hide", opacity: "hide"}, 500);
                        }
                    }
                    field.removeClass(classes[~~response.result]);
                    break;
                case "remove":
                    if (flag) {
                        field.removeData("flag");
                        if (flag.data("active")) {
                            flag.stop(true, true).animate({height: "hide", opacity: "hide"}, 100, function() {
                                flag.remove();
                            });
                        }
                    }
                    field.removeClass(classes[~~response.result]);
                    field.removeData("response");
                    break;
            }
        }
    }

});
由于堆栈溢出仅在与需要验证的输入交互时发生,并且
createField
函数仅用作初始化函数,因此我认为它不是此函数

循环功能编号2:

function createField (field) {
    inputs = inputs.not(field);
    var method = field.attr('data-validation-method');
    var requires = field.attr('data-validation-requires');
    if (!!requires) {
        requires = requires.split(',');
        var dependancies = {};
        $.each(requires, function(key, value) {
            var element = $('#' + value);
            if(element.length) {
                dependancies[element.attr('id')] = element;
                if(inputs.find(element).length) {
                    createField(element);
                }
                if ($.isArray(element.data('linked_fields'))) {
                    element.data('linked_fields').push(field);
                } else {
                    element.data('linked_fields', [field]);
                }
            }
        });
    }
    if (methods[method]) {
        fields = fields.add('#' + field.attr('id'));
        field.data('method', method);
        field.data('dependancies', dependancies);
    }
}
function validate (field) {
    var callback = function(response) {
        field.data('response', response);
        if (response) {
            toggleFlag(field, 'show');
        } else {
            toggleFlag(field, 'remove');
        }
        if($.isArray(field.data('linked_fields'))) {
            $.each(field.data('linked_fields'), function(key, value) {
                validate(value);
            });
        }
    }
    methods[field.data('method')](field, field.data('dependancies'), callback);
}

我没有权限使用任何其他外部程序来调试这个(公司环境),有人能告诉我正确的方向吗?

每当您使用jQuery来
addClass
removeClass
时,Internet Explorer就会触发事件
propertychange
。问题代码如下:

     var field = $(this);
        field.data("value", field.val());
        field.bind("propertychange keyup input paste", function(event){
            if(field.data("response") && (field.val() != field.data("value"))) {
                toggleFlag(field, "hide");
                if($.isArray(field.data('linked_fields'))) {
                    $.each(field.data('linked_fields'), function(key, value) {
                        toggleFlag(value, "hide");
                    });
                }
            }
      });
toggleFlag
函数中,调用jQuery的
addClass
removeClass
。这创建了无限递归循环,导致堆栈溢出

如果您去掉
属性更改
,它在Internet Explorer以及所有其他浏览器上都非常有效

工作示例:

仅在Internet Explorer上出现此问题的原因是,这是Microsoft为Internet Explorer实施的专有事件。它不是由其他浏览器实现的

使用IE6-8调试堆栈溢出:

function createField (field) {
    inputs = inputs.not(field);
    var method = field.attr('data-validation-method');
    var requires = field.attr('data-validation-requires');
    if (!!requires) {
        requires = requires.split(',');
        var dependancies = {};
        $.each(requires, function(key, value) {
            var element = $('#' + value);
            if(element.length) {
                dependancies[element.attr('id')] = element;
                if(inputs.find(element).length) {
                    createField(element);
                }
                if ($.isArray(element.data('linked_fields'))) {
                    element.data('linked_fields').push(field);
                } else {
                    element.data('linked_fields', [field]);
                }
            }
        });
    }
    if (methods[method]) {
        fields = fields.add('#' + field.attr('id'));
        field.data('method', method);
        field.data('dependancies', dependancies);
    }
}
function validate (field) {
    var callback = function(response) {
        field.data('response', response);
        if (response) {
            toggleFlag(field, 'show');
        } else {
            toggleFlag(field, 'remove');
        }
        if($.isArray(field.data('linked_fields'))) {
            $.each(field.data('linked_fields'), function(key, value) {
                validate(value);
            });
        }
    }
    methods[field.data('method')](field, field.data('dependancies'), callback);
}
将来诊断这些类型的堆栈溢出的一个好方法是:

  • 确定无限递归循环涉及的函数之一。如果你坚持使用IE6-8而没有调试功能,那么这就需要在各种函数中放置警报,直到你找到一个无限循环函数

  • 将这行代码放在函数顶部:

    alert(arguments.callee.caller.toString())

  • 此警报将告诉您哪个函数正在调用无限循环函数。然后,通过跟踪无限递归循环中涉及的函数,可以隔离代码中需要仔细检查无限循环原因的部分

    当然,如果您有一个带有适当调试工具的现代web浏览器,这是不必要的——您只需逐步完成代码即可


    顺便说一句,我感觉到了你的痛苦。我的部分工作还涉及为公司客户编写JavaScript,其中IE6-8通常是IT部门强制使用的浏览器。没有调试工具,只有警报和注释;当您对堆栈溢出进行故障排除时,甚至没有行号可供使用。

    您的公司环境不允许您使用完成工作所需的工具?@Michael My corporate environment将允许我使用几个月后完成工作所需的工具,只要这些工具经过测试,并且我的请求通过了大量的测试(非技术性)团队。到那时,这将无关紧要。我认为这样的故事是我能给你的最好的链接。相关:你试过Microsoft脚本编辑器吗?我听说是(在某些方面)比IE9/10 devtools更好。特别是,如果只需要测试某个部分,您可以拖动执行指针。我希望,但我的计算机不是问题所在(我可以在本地安装我想要的任何调试工具),而是我必须通过Citrix远程登录到公司客户机系统(或现场)诊断某些特定于其安装的问题。这些计算机通常被IT服务锁定,安装脚本编辑器或其他东西通常不是一个选项。你应该获得一枚金牌,我梦想着现代网络浏览器,很高兴我不是唯一一个。我从未想过这一点,我非常感激ul提供的深入分析和提示。