Bug Tracker

Opened 10 years ago

Closed 10 years ago

#9872 closed bug (wontfix)

$.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:


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.

Change History (10)

comment:1 Changed 10 years ago by Rick Waldron

Component: unfiledcore
Priority: undecidedlow
Resolution: invalid
Status: newclosed

The behaviour is not unexpected


(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.

comment:2 Changed 10 years ago by timmywil

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

comment:3 Changed 10 years ago by cmoschini

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

comment:4 Changed 10 years ago by ajpiano

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);


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

comment:5 Changed 10 years ago by cmoschini

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.

comment:6 Changed 10 years ago by Rick Waldron

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


comment:7 Changed 10 years ago by cmoschini

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

Last edited 10 years ago by cmoschini (previous) (diff)

comment:8 Changed 10 years ago by dmethvin

@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?

comment:9 Changed 10 years ago by Rick Waldron

Resolution: invalid
Status: closedreopened

comment:10 Changed 10 years ago by Rick Waldron

Resolution: wontfix
Status: reopenedclosed
Note: See TracTickets for help on using tickets.