Opened 9 years ago
Closed 8 years ago
#14411 closed feature (migrated)
Investigate implementing jQuery (specifically .init) as an Array subclass
Reported by: | gibson042 | Owned by: | gibson042 |
---|---|---|---|
Priority: | low | Milestone: | 1.next/2.next |
Component: | core | Version: | 1.10.2 |
Keywords: | Cc: | Rick Waldron | |
Blocked by: | Blocking: |
Description
Inspired by an interesting Zepto proposal that seems to have legs for us: http://jsperf.com/jquery-prototype-array/4
I suspect that it could reduce size as well, especially in 2.x.
Change History (12)
comment:1 Changed 9 years ago by
Component: | unfiled → core |
---|---|
Milestone: | None → 1.next/2.next |
Priority: | undecided → low |
comment:2 follow-up: 3 Changed 9 years ago by
Cc: | Rick Waldron added |
---|
comment:3 Changed 9 years ago by
Replying to dmethvin:
It won't work in oldIE, at least. The
.length
property is squirrely; jeresig tried it around 1.1 timeframe and bailed. @rwaldron wasn't this something still being refined by TC39 for ES6?
In ES6, it will be as simple as:
class jQuery extends Array { constructor(selector, context = document) { super(); // this is now a legit subclass of Array. } // ... implement jQuery prototype methods here }
The issue of course is that new subclass semantics can't be "polyfilled" or "ported" to traditional function declaration/expression syntax, because "super" can't be reserved in a function body.
While jQuery was actually quite vocal in the design and specification of this mechanism, we did so understanding that jQuery-the-code wouldn't benefit from real subclassing of builtins for some time.
As far as hackish cases like Zepto, proto is never going to be officially standardized, as it has been sent to die in an annex. Instead, Object.setPrototypeOf will compliment Object.getPrototypeOf.
comment:4 follow-up: 5 Changed 9 years ago by
Owner: | set to gibson042 |
---|---|
Status: | new → assigned |
I'm petty sure I already solved the oldIE length
issues in the jsperf by accepting immutability on existing instances (which doesn't change any current API promises), but I'll look into it more later. This is mostly just a focal point for link aggregation until and unless it gets to an actual pull request.
comment:5 Changed 9 years ago by
Replying to gibson042:
I'm petty sure I already solved the oldIE
length
issues in the jsperf by accepting immutability on existing instances (which doesn't change any current API promises), but I'll look into it more later.
There are actually 2 length related issues:
- In oldIE, trailing commas produce a "hole"
- In every standard, modern JS implementation, Array sub-classes will always have a broken length property.
Compare the following programs:
var a = new Array(1, 2, 3, 4); console.log( a ); // [ 1, 2, 3, 4 ] a.length = 10; console.log( a ); // [ 1, 2, 3, 4, , , , , , ] console.log( a.length ); // 10 a.length = 2; console.log( a ); // [ 1, 2 ] console.log( a.length ); // 2
vs.
function List() { Array.call(this); this.push.apply(this, arguments); } List.prototype = Object.create(Array.prototype, { constructor: { value: List } }); var l = new List(1, 2, 3, 4); console.log( l ); // [ 1, 2, 3, 4 ] l.length = 10; console.log( l ); // [ 1, 2, 3, 4 ] console.log( l.length ); // 10 l.length = 2; console.log( l ); // [ 1, 2, 3, 4 ] console.log( l.length ); // 2
This behaviour will be broken with __proto__
as well:
function List() { this.__proto__ = Array.prototype; this.push.apply(this, arguments); } var l = new List(1, 2, 3, 4); console.log( l ); // [ 1, 2, 3, 4 ] l.length = 10; console.log( l ); // [ 1, 2, 3, 4 ] console.log( l.length ); // 10 l.length = 2; console.log( l ); // [ 1, 2, 3, 4 ] console.log( l.length ); // 2
And of course, the latter version won't work in browsers that don't support proto.
Unrelated to those specific issues, I think inheriting from Array will create a lot of confusing API quirks. Consider the following:
- Same APIs where jQuery's definition doesn't match the built-in:
- jQuery.fn.filter and Array.prototype.filter (predicate functions written for use with Array.prototype.filter won't work with jQuery.fn.filter)
- jQuery.fn.map and Array.prototype.map (mapping functions written for use with Array.prototype.map won't work with jQuery.fn.map)
- jQuery.fn.find and Array.prototype.find (ES6). Same as "filter"
comment:6 Changed 9 years ago by
I'm not sure how any of these problems are relevant. As gibson042 already said, this "doesn't change any current API promises". Nobody is doing var divs = $( "div" ); divs.length = 10;
And it doesn't matter if our methods are incompatible with Array.prototype
methods, unless the incompatibility will actually break things.
comment:7 Changed 9 years ago by
I'm not seeing the perf benefit, strange. http://wnd8.com/grab/c42af8.png
comment:8 Changed 9 years ago by
Replying to scott.gonzalez:
I'm not sure how any of these problems are relevant.
They are exactly relevant to these two statements:
"The .length property is squirrely; " - dmethvin
"I already solved the oldIE length issues in the jsperf by accepting immutability on existing instances" - gibson042
Nobody is doing
var divs = $( "div" ); divs.length = 10;
I agree, I'm sure they aren't—but I didn't say any was, I was just trying to thoroughly illustrate the "length problems".
And it doesn't matter if our methods are incompatible with
Array.prototype
methods, unless the incompatibility will actually break things.
I'm not saying it will break extant code, I'm saying that the behaviour could create confusion.
comment:9 follow-up: 10 Changed 9 years ago by
To be clear, I'm interested in performance improvement and size reduction. We've never recommended that anyone set .length
(or documented the effects of doing so), and we generally can't expose Array methods because our patterns are to accept selectors in place of elements and pushStack
new instances instead of mutating context. Nor will we make new promises like Object.prototype.toString( jQuery() ) === "[object Array]"
... this is exclusively about seeking more efficient techniques for implementing our extant API.
comment:10 follow-up: 11 Changed 9 years ago by
Replying to gibson042:
To be clear, I'm interested in performance improvement and size reduction. We've never recommended that anyone set
.length
(or documented the effects of doing so),
Yes, I thought I made it clear that this was understood? http://bugs.jquery.com/ticket/14411?replyto=9#comment:8 My point is that once we say "jQuery is now subclassed from Array", developers will expect these things to work like they do with arrays.
and we generally can't expose Array methods because our patterns are to accept selectors in place of elements and pushStack new instances instead of mutating context. Nor will we make new promises like Object.prototype.toString( jQuery() ) === "[object Array]"...
They will be exposed by default and developers will use them. Unless you plan to keep a blacklist of methods to delete? jQuery instance objects will become instanceof Array as well.
I promise I'm not trying to rain on your parade, these are just real things to consider.
comment:11 Changed 9 years ago by
Replying to rwaldron:
My point is that once we say "jQuery is now subclassed from Array", developers will expect these things to work like they do with arrays.
They will be exposed by default and developers will use them. Unless you plan to keep a blacklist of methods to delete? jQuery instance objects will become instanceof Array as well.
We never have to crow about that, and we can continue chastising everyone who tries to take advantage of undocumented implementation details like we currently do with jQuery.support
and jQuery.data
.
I promise I'm not trying to rain on your parade, these are just real things to consider.
Yep, and I'm actually with you. Changing our prototype chain would be a huge deal... but it might be worthwhile.
comment:12 Changed 8 years ago by
Resolution: | → migrated |
---|---|
Status: | assigned → closed |
Migrated to https://github.com/jquery/jquery/issues/1754
It won't work in oldIE, at least. The
.length
property is squirrely; jeresig tried it around 1.1 timeframe and bailed. @rwaldron wasn't this something still being refined by TC39 for ES6?