| Page 1 of 2 | [ 15 posts ] | Go to page 1, 2 Next |
|
YUI cuts off prototype properties of configuration objects, which seems like an unnecessary inconvenience. Here is an example of what I meant.
It’s because of things like this in Base: Code: for (attr in attrs) { if (attrs.hasOwnProperty(attr)) { So, when I’ve done this: Code: var base_config = { visible: false }; var instance_a_config = Object.create(base_config); instance_a_config.width = "600px"; var instance = new Y.Widget(instance_a_config); …then “instance” will not have visible:false, since the hasOwnProperty() check weeds it out. This doesn’t seem very useful or desirable. As I understand, the intent of the hasOwnProperty() check in YUI is to prevent Object.prototype extensions from bleeding into all object iterations, esp. from Prototype.js. That being the case, ISTM it would be an improvement to check for *exactly* that, rather than killing all inherited properties. Something like: https://gist.github.com/1351541 Then, we can do this (which seems easier to read anyhow): Code: for (var key in obj) { if (!Object.gave(key,obj)) { … } …and we weed out anything added to Object.prototype, without killing off any other inheritance that implementers (ahem!!) may have in mind. The hasOwnProperty() check seems to prevent implementers from using prototypal inheritance in this way. ISTM a better approach would be to be paradigm-agnostic. |
|
We are looking at adding a Y.Object.flatten (or something similar) to aid in this. You are a very unique case for this and by far not the norm. We would also have to implement Y.Object.gave in order to support more than newer browsers.
Have you filed a ticket? If you've already got a use case & you have a solution a ticket is the place to post this, even better a Pull Request |
|
I've taken Felipe's view on this in past conversations (https://github.com/rgrove/yui3/commit/6 ... ent-408918).
It's a trade-off. a) Do we want to remove the fragility [1] from our code, by iterating more than the top-most level in the prototype chain? b) Do we want to try and protect people from the fragility caused by external code futzing with native prototypes? Both a) and b) have more than a small chance of causing breakage in the real world. We're choosing to go with b) and spreading fragility throughout our system, in order to nanny external implementations. I don't think that's the right trade-off option to pick. There are way more ways in which people can futz with the native JS and DOM environment than just adding onto the Object prototype, and we don't try to nanny all those scenarios. It's a losing battle which we're fighting at the expense of the library's usability. Flattening objects has the same issues as deciding what the filtering criteria should be if we want to iterate more than the top level properties in the prototype chain. It doesn't really provide a solution in and of itself, but is probably good sugar to have. Regarding iteration: For most config iteration [2], whitelists can usually be applied, and I would suggest we change to the non-HOP whitelist mix which I did for parts of Base/Attribute (see github commit discussion link above). For non-whitelist/open-ended iteration, if we really want to try and play nanny for implementations which mess with Object prototype, we could do something like this which seems like it would probably work for our use cases with minimal overhead, and works across A-Grade: var OBJECT = {}, p; for ( p in o ) { if ( !(p in OBJECT) ) { // o[p] is good to work with. } } http://yuiblog.com/sandbox/yui/v340/nannying.html The only issue I see off the top of my head is potentially not iterating native enumerable properties from Object.prototype, which it doesn't seem like we want to do anyway. There may be other issues. Satyen [1] Fragility of the type Felipe hit. And by any definition, it is fragility. When you pass an object to a method which asks for an object, you expect the object to be worked on as a whole, not just it's top-most prototype properties - that's just an arbitrary artifact of the nannying trade-off path we chose. [2] This conversation is not limited to configs of course. |
|
sdesai wrote: var OBJECT = {}, p; for ( p in o ) { if ( !(p in OBJECT) ) { // o[p] is good to work with. } } This runs afoul of an edge case where we add a key to Object.prototype that we’ve also added directly to the object, or to an intermediate ancestor … which is why I opted for a prototype chain traversal. With the simple “in” check that you propose here, there would be no way to tell YUI that, yes, even though the prototype has this key, I’ve also added the key to the object itself and *really* do want to include that in the iteration. I wonder if it would be useful to have: Y.giver(object, key) …which would return the “source” of object[key]. Then you could check: Code: for (key in obj) if (Y.giver(obj,key) !== Object.prototype) { … } I don’t know what the potential performance hit is versus the simpler mechanisms in place in YUI currently (HOP) or what Satyen proposes, but ISTM that YUI should accommodate as many use cases as possible, in which case it should specifically weed out properties that come from Object.prototype, and only those. |
|
As mentioned, the simpler version I provided is designed to weed out properties we may iterate over due to implementations messing with Object.prototype (which is what your edge case describes and is bad practice for the reasons we're discussing), as a replacement for what we were trying to use hasOwnProperty for. That is, if, in your edge case, you don't mess with Object.prototype, then it should work fine.
Performance is a factor. A lot of this iteration happens along critical paths [events, attribute, base etc.] and traversing the prototype chain for each property we iterate is expensive, however A-Grade support is also a factor in using getPrototypeOf (or __proto___). I agree, ideally if we could identify non-native properties added to Object.prototype across the A-Grade, and just avoid those, that would be perfect. Short of that, *if* we really want to try and protect people from other implementations which mess with Object.prototype, then the above seems like a reasonable solution given the constraints and pros/cons on the table. Is the edge case you describe something in your actual implementation? |
|
The edge case isn’t something we’re actually doing, no. (We actually specifically avoid adding to Object.prototype … I still think augmenting that prototype should trigger a warning in ES5.)
For our needs, checking keys of an empty Object instance should work and is definitely better than the HOP check. Just for my own information, the concern with prototype traversal is purely a performance one, right? Doesn’t every A-grade browser have Object.getPrototypeOf() or .__proto__ ? |
|
I believe IE7 is still considered A-Grade and has no way to determine the prototype of a particular object.
|
|
This doesn’t work? (Haven’t tried it…)
Code: obj.constructor.prototype |
|
Only if you don't override constructor accidentally or maliciously.
|
|
It may be helpful to boil this discussion down to some simple truths.
1. There is no reliable way to determine the prototype of an object across all A-grade browsers. 2. Pollution of Object.prototype (or the prototypes of other objects) by misbehaving libraries and uneducated developers is a real-world problem. I've run into it myself even on tightly-controlled codebases where it was the last thing I'd expect, and YUI has had bugs filed by users whenever we've failed to be defensive about this. 3. Some users know that prototype pollution is not an issue in their codebase or in their specific use cases, or are willing to accept the risk of using unfiltered enumeration. Now, my opinions. Things that YUI does, especially in the core utilities, should be reasonably safe by default. Given the impossibility of automatically determining whether to include prototype properties in enumeration, the safest course of action is to use hasOwnProperty filtering by default. This is a tradeoff. We avoid the problem of prototype pollution, which can result in some pretty nefarious and hard-to-debug problems when there are multiple libraries on a page, but we introduce potential confusion for users who expect prototype properties to be enumerated. I think the latter problem is easier to debug than the former problem, especially if YUI is consistent about this behavior and documents it clearly. Even moreso because I think users in the latter camp are likely to have more advanced knowledge of JS, and are more capable of understanding what's happening. That said, YUI also needs to allow prototype property enumeration when it's explicitly desired. Whitelists are one way to do this, but serve a narrow use case. Flattening is another way, and serves a broader use case. Both require the user to explicitly opt into the functionality, but I think this is a good thing. It indicates clear intent and understanding, on the user's part, of what's about to happen, so it allows YUI to trust that the user knows what they're doing. I think this is a safe and pragmatic compromise. It may not give everyone exactly what they want, but giving everyone what they want all the time is impossible, and any library that tries to do that quickly devolves into an unmanageable tangle of inconsistencies and edge cases. |
| Page 1 of 2 | [ 15 posts ] | Go to page 1, 2 Next |
| You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum |
© 2006-2013 Yahoo! Inc. All rights reserved.
All code on this site is licensed under the BSD License unless stated otherwise.
About This Site · Security Contact Info
Powered by phpBB® Forum Software © phpBB Group