Closed Raynos closed 12 years ago
That looks interesting, I tried to get a shim for this before based on John Resig's blog post but couldn't get it to work cross browser, this version looks a little more robust however, thanks for pointing it out.
I've taken a closer look at this version of the shim and I'm not sure it can ever reliably work in IE. Because it eventually fallsback to checking constructor.prototype
the shim will return different things based on how the prototype object was defined.
For example if the prototype is defined without an object literal the result is as expected:
Foo = function () {}
Foo.prototype.bar = "BAZ"
foo = new Foo ()
foo.constructor.prototype.bar // => "BAZ"
However if the prototype is defined using an object literal the constructor prototype points to Object.prototype
Foo = function () {}
Foo.prototype = {
bar: "BAZ"
}
foo = new Foo ()
foo.constructor.prototype.bar // does not return "BAZ"
I have set up a little test that demonstrates this here.
If this is correct then I'm not sure it is a good idea to include the shim as part of augment, ideally all shims should behave very closely to the native implementations. Otherwise you will get different behaviour for the same methods depending on which browser they run in.
I'm more than willing to be proved wrong however, if you can see a way to make this behave as expected I'd be very happy to include it as part of augment.
This is a generally-known problem (regarding the difference-in-operation on I.E. versus, well, the rest of the world); how about instead exposing a cross-browser-consistent version of the I.E. functionality called getConstructorPrototypeOf
, that will operate identically to getPrototypeOf
in well-behaved code (i.e. code that maintains the constructor.prototype.constructor
relationship), but at least operate predictably and consistently even on non-well-behaved code, in I.E. and elsewhere?
Thats an interesting approach to the problem. I don't think there is anyway to implement Object.getPrototypeOf in JavaScript in a cross browser way. I'm not sure however that adding non-standard methods to augment is the correct way to solve this problem.
I think your getConstructorPrototypeOf
might be more suited to supplement.js and I'd be more than happy to accept a patch to add it there. I feel that augment.js should only contain standard methods and only where they can be reliably implemented in JavaScript.
I’d agree, except that this is something that is “reliably implementable” everywhere except out-of-date IEs. In my opinion, out-of-date IEs “don’t matter,” beyond minimum efforts to make things work even to some minimal extent. Any sysadmin forcing an ancient IE upon their users is basically signing them up for the Shitty Web™, so there’s not a lot of point in wasting time/effort on it, or making a product of a lower quality overall just to pander to them.
… all that said, you, as the maintainer of this project, may disagree. ;D
Object.getPrototypeOf
is a simple return this.__proto__
in all non-IE browsers.
In IE browsers it's a simple return this.constructor.prototype
The only case it breaks if people use new Constr
but forget to set Constr.prototype.constructor
and then that's basically saying if you write bad code the method breaks.
The solution is warning people not to write bad code rather then not giving people a method that works.
@elliottcable - the main reason to use augment.js is to be able to seamlessly use the more modern JavaScript methods in old browsers such as IE. TBH if you are not supporting IE less than 8 you probably aren't getting a huge amount of benefit from augment.js since the browser share of old versions of safari, firefox etc are quite small.
@raynos - if the Constr.prototype.constructor
has been set then yes this does work. I must have been writing some bad code for quite a while now though as I don't think I've ever actually set that in my own code!
Perhaps this method could be added, with the caveat that you must set the constructor property in your prototype in IE if you are defining a prototype using an object literal. I guess without augment.js you would have to do this anyway to get similar behaviour.
Ideally I would like to be able to throw some kind of error in IE if Constr.prototype.constructor
has not been set, just so that you are forced to do this if you want to use this method in IE.
I'll try and put something together this week, I'd appreciate any feedback on the implementation once its done.
@olivernn that’s entirely my point. This only doesn’t work in the case of writing_bad_code LOGICAL AND developing_for_IE
, and IMO, there’s so little of an excuse for the entirety of the set union applying to those situations, that it’s entirely includable.
Not a big deal either way, I suppose. ^_^
Okay this is what I have so far, this does work in IE.
;(function () { "use strict"
var ensureIsObject = function (param) {
if (param !== Object(param)) throw new TypeError('Object.getPrototypeOf called on non-object');
}
if (!Object.getPrototypeOf) {
if (typeof "test".__proto__ === "object") {
Object.getPrototypeOf = function (obj) {
ensureIsObject(obj)
return obj.__proto__
}
} else {
Object.getPrototypeOf = function (obj) {
ensureIsObject(obj)
return obj.constructor.prototype
}
};
};
})();
I'm toying with the idea of adding a check to the IE version that throws a warning if you are setting the prototype using an object literal but haven't set the constructor
property. So far though the only way I can see to do this is something like this:
({}).constructor.prototype === obj.constructor.prototype
The problem with this would be if you were calling Object.getPrototypeOf
on a plain object. I'm not entirely sure why you would be doing this, but if you did want to, throwing an error here would mean you were screwed. Perhaps this check is unnecessary though, thoughts?
… that test is already incorrect, right off the bat, isn’t it? As (new new Function()()).constructor.prototype !== Object.prototype
. When you create a new function, a new prototype object is immediately created for it, is it not? Thus, there’s no real way to determine if <something>.constructor
’s .prototype
was changed after new <something>.constructor()
resulted in the construction. At least, that I can think of off the top of my head.
Yeah good catch - was just hoping that there was a way to catch the case where the prototype was assigned to an object literal but constructor hadn't been set. TBH trying to catch it will probably cause more problems then it would solve.
I'll try and put together a release sometime this week with Object.getPrototypeOf
included. There are a couple of other build related things I'd like to clean up as well though so it could be a couple of days.
I've just released version 0.4.0 of augment that includes the discussed implementation of Object.getPrototypeOf
.
See shim :
http://forrst.com/posts/Object_getPrototypeOf_shim-eNB
It might be worthwhile to add this to augment.