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

javascript - Chrome sometimes calls incorrect constructor - Stack Overflow

programmeradmin3浏览0评论

We have a web site that uses extensively jQuery and it works fine in Firefox and IE. However in Chrome, we are getting frequently (and semi-randomly) Uncaught TypeError: Cannot call method 'apply' of undefined (also other jQuery methods appear in place of apply).

We managed to track down the problem to jQuery method pushStack().

Original source code (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.)
}

Instrumented code:

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

In most cases pushStack() runs correctly. However sometimes Chrome constructs an object of type Object instead of jQuery.fn.init. Console output:

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>

Did anybody encounter similar problem? Is it a (known) bug of Chrome?

Update

I managed to simplify our page, so that it could be loaded on its own. I filled bug in Chromium project project, the page for reproducing the issue is attached there.

We have a web site that uses extensively jQuery and it works fine in Firefox and IE. However in Chrome, we are getting frequently (and semi-randomly) Uncaught TypeError: Cannot call method 'apply' of undefined (also other jQuery methods appear in place of apply).

We managed to track down the problem to jQuery method pushStack().

Original source code (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.)
}

Instrumented code:

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

In most cases pushStack() runs correctly. However sometimes Chrome constructs an object of type Object instead of jQuery.fn.init. Console output:

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>

Did anybody encounter similar problem? Is it a (known) bug of Chrome?

Update

I managed to simplify our page, so that it could be loaded on its own. I filled bug in Chromium project project, the page for reproducing the issue is attached there.

Share Improve this question edited Apr 26, 2012 at 7:18 Miroslav Bajtoš asked Apr 25, 2012 at 11:50 Miroslav BajtošMiroslav Bajtoš 10.8k1 gold badge42 silver badges100 bronze badges 6
  • Could you narrow down the use case? It sounds like the mon console.log.apply context problem. – David Hellsing Commented Apr 25, 2012 at 12:04
  • @David Thank you for your ment. What do you mean by console.log.apply context problem? The problem happens also when using original jQuery without instrumentation. – Miroslav Bajtoš Commented Apr 25, 2012 at 12:12
  • @MiroslavBajtoš Have you applied the workaround suggested in the Chromium project? – davidzarlengo Commented May 23, 2012 at 16:10
  • @davidzarlengo Yes, I am the author of that workaround. – Miroslav Bajtoš Commented May 23, 2012 at 17:27
  • Oh, well, in that case, thank you! So far, it's working for us as well. – davidzarlengo Commented May 23, 2012 at 21:08
 |  Show 1 more ment

2 Answers 2

Reset to default 11

The responses in Chromium bug tracker seem to confirm that this is a bug of Chrome browser.

The workaround solution is to "fix" pushStack() function in 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.
}

Here is an "unobtrusive" solution (for example, if you're pulling jQuery from a CDN). Save this in a .js file and include it after pulling in 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);
发布评论

评论列表(0)

  1. 暂无评论