Chrome иногда вызывает неправильный конструктор
У нас есть веб-сайт, который широко использует jQuery и отлично работает в Firefox и IE. Однако в Chrome мы часто (и полу-случайным образом) получаем Uncaught TypeError: Cannot call method 'apply' of undefined
(вместо apply
появляются другие методы jQuery).
Нам удалось отследить проблему до метода jQuery pushStack()
.
Исходный исходный код (jQuery 1.7.1):
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = this.constructor();
// (etc.)
}
Инструментальный код:
pushStack: function( elems, name, selector ) {
if (!(this instanceof jQuery.fn.init)) throw this;
// Build a new jQuery matched element set
var ret = this.constructor();
if (!(ret instanceof jQuery.fn.init)) {
console.log("pushStack>this: " + this.constructor);
console.log("pushStack>ret: " + ret.constructor);
throw ret;
}
// (etc.)
}
В большинстве случаев pushStack()
выполняется правильно. Однако иногда Chrome создает объект типа Object
вместо jQuery.fn.init
. Выход консоли:
pushStack>this: function ( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
}
pushStack>ret: function Object() { [native code] }
Uncaught #<Object>
Кто-нибудь сталкивался с подобной проблемой? Это (известная) ошибка Chrome?
Обновление
Мне удалось упростить нашу страницу, чтобы она могла быть загружена сама по себе. Я заполнил ошибка в проекте Chromium, страница для воспроизведения проблемы прилагается.
Ответы
Ответ 1
Отклики в Chromium bug tracker, похоже, подтверждают, что это ошибка браузера Chrome.
Обходным решением является "зафиксировать" pushStack()
функцию в jQuery:
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = this.constructor();
// Workaround for Chrome bug
if ((this instanceof jQuery.fn.init) && !(ret instanceof jQuery.fn.init)) {
// console.log("applying pushStack fix");
ret = new jQuery.fn.init();
}
// etc.
}
Ответ 2
Вот "ненавязчивое" решение (например, если вы вытаскиваете jQuery из CDN). Сохраните это в файле .js и включите его после вставки jQuery.
(function ($) {
var pushStackOrig, pushStackChrome;
pushStackOrig = $.fn.pushStack;
pushStackChrome = function ( elems, name, selector ) {
// Build a new jQuery matched element set
// Invoke the correct constructor directly when the bug manifests in Chrome.
//var ret = this.constructor();
var ret = new jQuery.fn.init();
if ( jQuery.isArray( elems ) ) {
push.apply( ret, elems );
} else {
jQuery.merge( ret, elems );
}
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" ) {
ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
} else if ( name ) {
ret.selector = this.selector + "." + name + "(" + selector + ")";
}
// Return the newly-formed element set
return ret;
};
$.fn.pushStack = function (elems, name, selector) {
var ret;
try {
ret = pushStackOrig.call(this, elems, name, selector);
return ret;
}
catch (e) {
if (e instanceof TypeError) {
if (!(ret instanceof jQuery.fn.init)) {
ret = pushStackChrome.call(this, elems, name, selector);
return ret;
}
}
throw e;
}
};
}).call(this, jQuery);