Skip to main content

Bug Tracker

Side navigation

#9872 closed bug (wontfix)

Opened July 20, 2011 04:52PM UTC

Closed July 20, 2011 10:12PM UTC

$.each(['selector'], function() { $(this) } produces unexpected behavior

Reported by: cmoschini Owned by:
Priority: low Milestone: None
Component: core Version: 1.6.2
Keywords: Cc:
Blocked by: Blocking:
Description

See this JSFiddle:

http://jsfiddle.net/87kKw/

This is unexpected behavior.

callback.call() in .each() appears to type the string as 'object' even though its type is string when passed in.

Things then go awry in .init(), on this line:

if ( typeof selector === "string" ) {

Since .call() is native, this could be resolved by changing the string test from typeof string to a property test like:

if ( selector.charCodeAt ) {

Which passes even when the string comes in from .each() or other .call() codepaths.

Attachments (0)
Change History (10)

Changed July 20, 2011 06:00PM UTC by rwaldron comment:1

component: unfiledcore
priority: undecidedlow
resolution: → invalid
status: newclosed

The behaviour is not unexpected

http://api.jquery.com/jquery.each/

(The value can also be accessed through the this keyword, but Javascript will always wrap the this value as an Object even if it is a simple string or number value.) The method returns its first argument, the object that was iterated.

Changed July 20, 2011 06:03PM UTC by timmywil comment:2

Also, the value is passed to each: http://jsfiddle.net/timmywil/87kKw/3/

Changed July 20, 2011 06:07PM UTC by cmoschini comment:3

What's unexpected is passing it to JQuery as a selector causes the code to fail, not the fact that $.each() wraps it as an object.

There are many cases you can take in what you know to be a string and happens to be typed as object (from JQuery functions and not) and JQuery produces this strange result with no exceptions thrown or obvious causes. The fix needed is in .init(), not .each().

Changed July 20, 2011 06:09PM UTC by ajpiano comment:4

Thanks for taking some time to help out the jQuery project, but I'm not sure this is something we'll intend to fix. As you have noted, jQuery does pass the boxed object at 'this' to the iterating function in $.each, passing a String object in this case. However, the actual string (with typeof string) is passed as an argument to the iterating function, and it's recommended that you use that instead of $(this);

http://jsfiddle.net/ajpiano/87kKw/2/

I'll wait for others to chime in to decide if handling String objects inside of .init, perhaps using a duck test like you described, is something we think we should tackle...but from the comments above it seems like a no...

Changed July 20, 2011 06:26PM UTC by cmoschini comment:5

Thanks ajpiano.

Ultimately all that matters to me is that the cause of this bug in codepaths like the above be more discoverable. Passing a string that happens to be typed as object isn't rare in Javascript, especially in callback/event handling situations, and so, in AJAX situations. As it stands this is a tough one to ferret out.

The code change I'm proposing is cheap in byte cost to the size of JQuery, introduces no negative impact to existing functionality, and prevents anyone from having to hunt down this sort of issue.

Right now the code falls through to:

return jQuery.makeArray( selector, this )

which definitely isn't what the developer intended, and largely leads to silent errors - hard to develop against.

Changed July 20, 2011 08:07PM UTC by rwaldron comment:6

As noted above, the docs explicitly state that |this| will be object form, if you want to use |this| as you've demonstrated, you will need to coerce it to a string, otherwise just use the val param

http://jsfiddle.net/rwaldron/HKPUC/

Changed July 20, 2011 08:10PM UTC by cmoschini comment:7

_comment0: Yes, rwaldron, but please read my entire comment - I'm talking about the problem of jQuery.init() taking a string that's really an object and producing strange results, not the problem of .each() (which is weird but just the nature of the native .call() method).1311192686730578

Yes, rwaldron, but please read my entire comment - I'm talking about the problem of jQuery.init() producing strange results when you pass a string that happens to be typed as an object; not the problem of .each() (which is weird but just the nature of the native .call() method).

Changed July 20, 2011 08:55PM UTC by dmethvin comment:8

@cmoschini, you're right that it's unexpected behavior, but I am not sure what more we can do about it. As @rwaldron pointed out, we do document that Javascript is going to box this as an Object and it's not something we control.

There are many places in jQuery where we look for primitive strings but not String objects, so checking in .init() doesn't really solve much except for this specific case. For example, .bind(events, ...) wants either a string of events or an Object of events/handlers. Once we start heading down this road I forsee people pointing back to #9872 and saying "You did it there, why not for my case too?"

With that in mind, I'd really like this to stay closed. Perhaps wontfix instead of invalid?

Changed July 20, 2011 10:12PM UTC by rwaldron comment:9

resolution: invalid
status: closedreopened

Changed July 20, 2011 10:12PM UTC by rwaldron comment:10

resolution: → wontfix
status: reopenedclosed