Chrome有时会调用错误的构造函数

我们有一个广泛使用jQuery的网站,它在Firefox和IE中运行良好。 但是在Chrome中,我们经常(并且半随机地)获得Uncaught TypeError: Cannot call method 'apply' of undefined (也可以使用其他jQuery方法代替apply )。

我们设法将问题pushStack() 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类型的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 # 

有没有人遇到类似的问题? 它是Chrome的(已知)错误吗?

更新

我设法简化了我们的页面,以便它可以自己加载。 我填写了Chromium项目项目中的错误,重现问题的页面附在那里。

Chromium bug跟踪器中的响应似乎证实这是Chrome浏览器的错误。

解决方法是在jQuery中“修复” pushStack()函数:

 // 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. } 

这是一个“不引人注目”的解决方案(例如,如果你从CDN中提取jQuery)。 将其保存在.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);