Closed cpeterso closed 9 years ago
Thanks for letting us know. People in the bugzilla thread speak as this is the fault of people using __proto__
, but don't suggest viable alternatives using standard JavaScript. All we need is a fast method to create a Zepto collection:
$.fn
object.jQuery create objects that fit your criteria of a Zepto collection, obviously. The opposite would certainly lead to strong incompatibilities leading to Zepto not being a drop-in replacement. But jQuery never used __proto__
or anything non-standard. Why not using their technique? (I know the answer, don't bother ;-) )
__proto__
is not standard. It will be in ES6... de facto. I hope you know what de facto means in this context.
The more appropriate ES6 way of doing things involves subclassing arrays as in:
class Zepto extends Array{
...
}
Zepto.prototype
will be your $.fn
@DavidBruant How does jQuery do it? I've never looked.
That ES6 snippet you posted looks really nice but we need something that works now.
How about #736 by @webreflection? It’s an easy step to avoiding __proto__
where possible.
@mathiasbynens Polyfilling Object.setPrototypeOf
with __proto__
has the same downside, especially performance-wise. The heart of the problem is the Zepto.Z function setting __proto__
(directly or after a function call). Getter accesses to __proto__
aren't that big of a deal.
@mislav
@DavidBruant How does jQuery do it? I've never looked. That ES6 snippet you posted looks really nice but we need something that works now.
jQuery's technique did work at the time Zepto was created, too bad you never cared to look :-/ No need to debate this further anyway. Zepto made a decision while there were alternatives (Zepto is supposed to be a jQuery replacement and jQuery source is available). The rest is arguing nanoseconds and nanobytes or bad faith.
@mathiasbynens Object.setProtoypeOf
isn't supported anywhere, and when it will be there's no guarantee that it will be performant. Additionally, if a browser does implement performance optimizations for it, they should apply to __proto__
as well, as it's doing the same thing.
@DavidBruant Instead of truisms, please suggest a method of speeding Zepto up, for example as a proof-of-concept patch.
@DavidBruant & @mathiasbynens :
Does the ES6 standard still allow us to define classes and objects the traditional ways ( http://www.phpied.com/3-ways-to-define-a-javascript-class/ )? Of are they going to deprecate those as well?
And in case they still allow defining classes and objects the traditional ways, what exactly does this new syntax offer that we don't already have? How is the new class syntax an improvement beyond mere readability for people used to writing code in languages like Java or PHP?
@jslegers This is just my opinion and I only speak for myself here:
Does the ES6 standard still allow us to define classes and objects the traditional ways ( http://www.phpied.com/3-ways-to-define-a-javascript-class/ )?
Yes. Everything that has been based on standards and works will keep working likely for eternity.
Of are they going to deprecate those as well?
I disagree with @cpeterso when he wrote that __proto__
will be deprecated. "deprecated" does not mean much in web technologies since breaking backward compatibility is impossible on the web. But setting __proto__
will continue to be a discouraged practice. Among other things because it's much harder to optimize for implementors and it can lead to hard-to-read code (not in Zepto's case where the use is very localized).
TC39 and people on the es-discuss mailing list spent lot of time discussing the use cases and usages of setting __proto__
. This article by Kangax was of some influence. That's one of the reasons the class syntax emerged (the extends
keyword at least).
And in case they still allow defining classes and objects the traditional ways, what exactly does this new syntax offer that we don't already have?
Subclassing builtins like Array (exactly Zepto's use case) or Date. I think there are subtle things related to super
too, but I didn't look at this too closely yet.
How is the new class syntax an improvement beyond mere readability for people used to writing code in languages like Java or PHP?
Readability too indeed. I'm not a big Java or PHP dev, but I've played with TypeScript and appreciated the class syntax. It leaves no ambiguity as to the intention of the code. Instead of having to figure out "oh yeah, this function is a constructor and here they're building the prototype and it inherits from X", the class syntax makes it straightforward. You don't have to reverse-engineer the class pattern. The class syntax makes "official" a very common pattern. In large code bases, that makes a difference. For people who come to the language and don't want to spend hours investigating how to do code reuse, the class syntax will save them time and do the right thing for them.
@jslegers Don't get me started about ES6, I think it's an attempt to please everyone, ending up with a language that doesn't know what it wants. There's a few good bits in there, but traditionally JavaScript has been a language that evolved slowly, adding a few features here and there, That's served it well, it's arguably the most-installed and one of the most-used languages today. The attempt to "revolutionize" it will, in all likelihood, fail.
@DavidBruant :
So what exactly do the standards say about extending builtins today? Last week, I experimented with the following syntax, in an attempt to add a cross-browser event mechanism for altering the DOM :
(function(element) {
element.appendChild = function(old_remove) {
return function(childNode) {
this.dispatchEvent(new CustomEvent('eventAppendChild', {
'detail': {
'node': childNode
}
}));
return old_remove.call(this, childNode);
}
}(element.appendChild);
})(Element.prototype);
Quite to my surprise, I managed to get this technique to work in both Chrome and IE8 (using a polyfill for events) for every DOM mutation method I tried, but Firefox silently failed when I used it on 'setAttribute', 'removeAttribute'. I'm a bit puzzled on the reason for Firefox's different behavior on specificly those methods.
@madrobby :
The main problem I have with ES6 standards is that their implementation often involves the deprecation of useful non-standard features like proto, noSuchMethod, defineGetter or mutation events in favor for newer, standardized syntaxes. By effectively removing such features in newer browsers, doing anything remotely advanced in a cross-browser fashion becomes no less a mess than it was in the days of NS4 and IE5.5 as it forces you to either polyfill the new syntax with the old syntaxes or add if/else statements for testing the various different syntaxes.
I'm all in favor of standards and am very much looking forward to actually being able to use something as cool as Proxies, but deprecating non-standard features for which no decent alternatives exist other than the ES6 standards seems like a very bad move to me at a time when ES6 is still in a draft stage and you still need to explicitly turn on the 'Enable Experimental JavaScript' feature in Chrome for quite a number of its ES6 features... noSuchMethod, where are you when I need you?!
@jslegers I feel we're getting a bit off-topic, but in short, there is a distinction between "builtins" defined in the ECMAScript spec and the ones defined in W3C specs. This has nothing to do with the specs themselves, but the implementations and teams in browser engines which were different for both technologies. ES6 proposes a mechanism to extend ECMAScript builtins, but says nothing about DOM builtins (and shouldn't anyway). The spec trying to gap W3C "interfaces" and their reification in JavaScrip is WebIDL. For now, I don't think they're discussing extending Element. Note that WebComponents are a mechanism to extend HTMLElement. On your specific code, I don't know, did you raise a bug on the Firefox side? If they don't know, they'll never fix it...
The main problem I have with ES6 standards
The expression "ES6 standards" doesn't have a meaning. ES6 will be one standard. ES6 does not deprecate anything. In general, specs never remove anything (there are some very rare exceptions).
deprecating non-standard features for which no decent alternatives exist other than the ES6 standards seems like a very bad move to me
If you have use cases that aren't covered by standard features, please raise your voice to es-discuss. Some people from TC39 go in developer conferences to ask for feedback and very few people send some. For sure they won't visit every random github issue to find the feedback they need from developers. I strongly insist on bringing use cases and not features
__noSuchMethod__
, where are you when I need you?!
A decent share (the only useful share some would argue) of __noSuchMethod__
can be reimplemented with proxies.
http://soft.vub.ac.be/~tvcutsem/proxies/ contains an example with the old Proxy API (but it can be adapted easily to the new API)
This is a great discussion about the future of JavaScript, guys. However, it fails to address two main topic points:
__proto__
entirely, being a nonstandard property that the Firefox team isn't likely to care about in the future.PRs (actual code) welcome
@DavidBruant :
I haven't raised a bug yet. I'm not even sure if it's worthy to pursue such a hacky technique without solid cross-browser support.
My use case : creating a library that provides a very easy and intuitive syntax for two-way data binding between any two variables (including DOM properties).
For example, this is one syntax I've been considering :
var usersDATA = [
{'name' : 'John', 'city' : 'Leuven' },
{'name' : 'David', 'city' : 'Bordeaux' }
];
var usersHTML = document.getElementById('table-users');
binder.set({
'key' : 'userhtml-to-data',
'source' : usersHTML,
'target' : usersDATA,
'filter' : function(source, target) {
// object that transforms the 'source' format to the 'target' format
}
}, {
'key' : 'userdata-to-html',
'source' : usersDATA,
'target' : usersHTML,
'filter' : function(source, target) {
// object that transforms the 'source' format to the 'target' format
}
});
The problem I'm experiencing, is finding a way to capture changes to the 'source' that doesn't involve polling and that is both performance and user friendly. An observer pattern using custom JS events seemed a natural choice, but it's been giving me a lot of headaches trying to actually implement it :
And yes, I agree with you and @mislav that this probably isn't the best place to discuss this. I'm also not sure es-discuss is a better place. Feel free to mention any more suitable place if anyone's interested in continuing it. I'd love to know about any technique I may be overlooking to achieve what I'm trying to achieve...
mislav, is the requirement that setting indexed properties magicall update the length, or simply that reading indexed properties work?
In the latter case, just creating an object whose proto is $.fn would work fine; you can use square bracket indexed properties on any JS object. The main magic with arrays is the auto-update of length and having Array.prototype on the proto chain (which you presumably still do if it's on the proto chain of $.fn).
If you're in the former case, I'm still looking into what your options are.
@bzbarsky No, I don't think you should be able to mutate the Zepto collection. We use some array methods on Zepto collections internally but I don't consider that to be public interface. Your idea is good, since length
doesn't need to be magically updating.
The main advantage of __proto__
is that there's no copying of objects required. We should be sure to benchmark large Zepto collections (hundreds or thousands of items).
The point is to creat the object with the correct prototype up front instead of creating it and then mutating its prototype chain. In either case you end up with the same prototype chain in the end.
Something along the lines of (just describing the structure):
var $ = {fn: Object.create(Array.prototype)}
function Zepto(){}
Zepto.prototype = Object.create($.fn);
var zeptoCollection = new Zepto()
Advice from @jdalton :
Zepto goes nodelist->array via slice->proto to zepto. It could go nodelist->zepto via push
__proto__
is no longer used since 24ebb7aa073775f38ab8b4158fcbc90bc0594e2a.
The following Zepto 1.0 vs 1.1 selector test is 1.8x slower in Firefox 26 than Firefox 25:
http://jsperf.com/zepto-1-0-vs-1-1-performance/7
The primary problem is that the
zepto.Z
function overridesdom.__proto__
, which prevents many JIT optimizations. Overriding an object's__proto__
property is strongly discouraged and will be deprecated in ES6.For more information, please see Firefox bug 947048 for Mozilla developers' discussion this particular Zepto test regression.