Skip to content

Core: Make jQuery objects iterable #2369

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 13, 2015
Merged

Core: Make jQuery objects iterable #2369

merged 3 commits into from
Jun 13, 2015

Conversation

mgol
Copy link
Member

@mgol mgol commented Jun 1, 2015

Make iterating over jQuery objects possible using ES 2015 for-of:

for ( node of $( "<div id=narwhal>" ) ) {
    console.log( node.id ); // "narwhal"
}

Fixes gh-1693

TODO:

  • Add a Node.js test checking if the iterator is defined even for core-js-polyfilled Symbol & Symbol.iterator.
  • Add tests ensuring that the iterator works with babel + core-js polyfills.
  • Rename jsHint to JSHint in comments.
  • Drop the Symbol.iterator check, leave just the typeof Symbol one.
  • Switch [][Symbol.iterator] to arr[Symbol.iterator].
  • Use QUnit.skip to skip the test in non-ES6 browsers. Just use the ok assertions in non-ES6 browsers.
  • Split the JSHint upgrade to a separate commit (to do just before landing).

@rwaldron, could you have a look if it's safe to include this in 3.0.0?

// unguarded in another place, it seems safer to just disable jsHint for these
// three lines.
/* jshint ignore: start */
if ( typeof Symbol === "function" && Symbol.iterator ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just check for Symbol on the global instead of directly as a global?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we don't have a reference to the global, we only have reference to window. In the browser they're equivalent but we also support running jQuery on Node (in a very basic way but still) where window is a fake window object created by a library like jsdom.

To reference a global we'd need to pass an additional parameter to the jQuery factory... but that wouldn't work since if used with Browserify this references the module and not the global. So we'd basically need to run a cascade test - check for the global variable, fallback to this. This wouldn't be shorter and would certainly be more fragile.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I didn't realize we didn't have a reference to the global.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check typeof Symbol.iterator === 'symbol'?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to worry about cases where a global Symbol is defined as a
function and Symbol.iterator doesn't have a typeof equal to "symbol"? I
imagine this would break using this code with Symbol polyfills that can't
override typeof.

Michał Gołębiowski

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and advocate for letting jQuery be exactly as iterable as arrays sharing its environment (and hence not to check for Symbol.iterator at all).

Almost agree. If there were an environment that implemented Symbol, but hadn't implemented Symbol.iterator yet, you'd end up with jQuery.fn.undefined = undefined. If you don't care about that, then it's all good and I agree. Also, I don't know of any runtimes that match that, but for a few months both Chrome and Firefox did (obviously not anymore).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there were an environment that implemented Symbol, but hadn't implemented Symbol.iterator yet, you'd end up with jQuery.fn.undefined = undefined.

Correct. Or perhaps even something a little weirder, if Symbol.iterator is partially implemented (e.g., by incomplete polyfill).

If you don't care about that, then it's all good and I agree.

Correct. I don't care about that, and here's why (edit mine):

I don't know of any runtimes [supported by jQuery] that match that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current situation is as follows:

  1. Latest stable Firefox, Chrome & Opera support Symbol, Symbol.iterator & for-of on any collections.
  2. IE11 supports neither Symbol nor for-of but Edge supports both.
  3. Safari 8 doesn't support Symbol; it supports for-of only on Arrays. WebKit Nightly supports Symbol, Symbol.iterator & for-of on any collections.

I don't think we're in a lot of danger that any browser will arrive with that much broken Symbol global judging by the above info.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mzgol as always, thank you for the thorough follow up

@rwaldron
Copy link
Member

rwaldron commented Jun 2, 2015

👏 This is so awesome.

// three lines.
/* jshint ignore: start */
if ( typeof Symbol === "function" && Symbol.iterator ) {
jQuery.fn[ Symbol.iterator ] = [][ Symbol.iterator ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use arr instead of [].

@mgol
Copy link
Member Author

mgol commented Jun 4, 2015

PR updated, please re-review.

@@ -10,3 +10,5 @@

/dist
/node_modules

/test/node_smoke_tests/lib/ensure_iterability.js
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could put it all in dist and not add anything to .gitignore or .jshintignore but then I'd have to copy the entire test/node_smoke_tests directory over as the ensure_iterability_es6.js file requires files relative to its original location so transpiling to a different directory wouldn't fly.

"grunt-cli": "0.1.13",
"grunt-compare-size": "0.4.0",
"grunt-contrib-jshint": "0.10.0",
"grunt-contrib-jshint": "0.11.2",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required so that typeof x === "symbol" works in esnext mode.


if ( result !== "DIVSPANA" ) {
console.error( "for-of doesn't work on jQuery objects; " +
"expected: 'DIVSPANA'; got: '" + result + "'" );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably use assert, but I'm fine with a later sweep of node_smoke_tests for that.

@gibson042
Copy link
Member

LGTM 👍

@mgol
Copy link
Member Author

mgol commented Jun 8, 2015

I'll land this shortly barring any new remarks.

mgol added 3 commits June 13, 2015 22:45
Utilize the assert module, avoid inline JSHint comments.
Make iterating over jQuery objects possible using ES 2015 for-of:

    for ( node of $( "<div id=narwhal>" ) ) {
        console.log( node.id ); // "narwhal"
    }

Fixes jquerygh-1693
@mgol mgol merged commit bb026fc into jquery:master Jun 13, 2015
@mgol mgol deleted the for-of branch June 13, 2015 21:20
@mgol mgol removed the Needs review label Jul 31, 2015
@lock lock bot locked as resolved and limited conversation to collaborators Jan 19, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Make jQuery collections for-of-able