Pretty simple:
I have code using Object.getPrototypeOf(...) to get the inherited classes of a Dojo Widget (just a JS object). Object.getPrototypeOf(...) isn't supported in IE8. I need an IE work around. Any ideas? Thanks in advance.
Jon Resig's polyfill works
http://ejohn.org/blog/objectgetprototypeof/
I have made it even smaller
if (typeof Object.getPrototypeOf !== "function")
Object.getPrototypeOf = "".__proto__ === String.prototype
? function (object) {
return object.__proto__;
}
: function (object) {
// May break if the constructor has been tampered with
return object.constructor.prototype;
};
Use https://github.com/kriskowal/es5-shim. Among other things, it supports Object.getPrototypeOf.
Source: ECMAScript 5 polyfills from Modernizr project
Classes created with Dojo.declared store metadata with their superclasses so you don't need to use getPrototypeOf.
I think you can get the first superclass with
MyClass.prototype.constructor._meta.bases[1]
and its prototype with
MyClass.prototype.constructor._meta.bases[1].prototype
(bases[0] seems to be the class itself)
Although why are you even needing to get the prototype? Its very likely you will end up reimplementing some feature that is already provided by dojo.declare
Related
Pretty simple:
I have code using Object.getPrototypeOf(...) to get the inherited classes of a Dojo Widget (just a JS object). Object.getPrototypeOf(...) isn't supported in IE8. I need an IE work around. Any ideas? Thanks in advance.
Jon Resig's polyfill works
http://ejohn.org/blog/objectgetprototypeof/
I have made it even smaller
if (typeof Object.getPrototypeOf !== "function")
Object.getPrototypeOf = "".__proto__ === String.prototype
? function (object) {
return object.__proto__;
}
: function (object) {
// May break if the constructor has been tampered with
return object.constructor.prototype;
};
Use https://github.com/kriskowal/es5-shim. Among other things, it supports Object.getPrototypeOf.
Source: ECMAScript 5 polyfills from Modernizr project
Classes created with Dojo.declared store metadata with their superclasses so you don't need to use getPrototypeOf.
I think you can get the first superclass with
MyClass.prototype.constructor._meta.bases[1]
and its prototype with
MyClass.prototype.constructor._meta.bases[1].prototype
(bases[0] seems to be the class itself)
Although why are you even needing to get the prototype? Its very likely you will end up reimplementing some feature that is already provided by dojo.declare
Is this even possible? How about other browsers? Any estimates when es6 will be "ready" and rolled out?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
btw. https://github.com/tvcutsem/harmony-reflect Proxy does not work with current chrome (36.0.n)
You could use Object.defineProperty and Object.observe to to simulate a Proxy. I started to wonder how much functionality a polyfill could support, so I wrote an implementation (you can see it at gist.github.com/mailmindlin/640e9d707ae3bd666d70).
I was able to emulate all of the features of Proxy that didn't rely on operator overloading, whic isn't possible in JavaScript as of now.
However, you can get the get, set, and a few others working. You can use getters and setters to mirror the target object's properties:
for (var property in target)
Object.defineProperty(proxy, property, {
get: function() {
if ('get' in handler)
return handler.get(target, property, proxy);
else
return target[property];
},
set: function(value) {
if ('set' in handler)
handler.set(target, property, value, proxy);
else
target[property] = value;
}});
The only problem with that is that the getters and setters only apply to properties that were defined in for the target when the proxy was initialized, and the delete operator won't work (If you delete a property on the target object, the proxy will still enumerate it as a property; if you delete a property on the proxy, nothing will happen to the object).
To fix that, you can use Object.observe which will be called on any change to either object. A quick check on caniuse.com shows that Object.observe is available on Chrome and Opera. If you really need support for Proxy on another browser, you can poll the target and proxy objects, to check if any properties have been created or destroyed:
var oldKeys = Object.keys(proxy);
setInterval(function() {
var keys = Object.keys(proxy);
for(var i in keys)
if(!oldKeys.includes(keys[i]))
//Trigger code for a new property added
for(var i in oldKeys)
if(!keys.includes(oldKeys[i]))
//trigger code for a deleted property
oldKeys = keys;
//repeat for target object
}, 100);
If you desperately need more features of the proxy, you can try overriding methods such as Object.defineProperty and Object.getOwnPropertyDescriptor, but that might create compatibility issues with other scripts, depending on how you do it.
In short, you can do most of what you'll probably need to use Proxy for with a polyfill. As far as Google adding it to their browser, I have no idea. It apparently used to be part of the V8 engine, but it was removed because of security problems (that nobody elaborates on), as far as I could tell based on this thread.
I have created babel plugin whic allows you to do this but it comes with huge performance impact (for each property access) - it is more education example.
https://github.com/krzkaczor/babel-plugin-proxy
Here is one created by the Google Chrome team:
https://github.com/GoogleChrome/proxy-polyfill
It's not a full implementation, though.
Update: Although my answer provides a partial solution, mailmindlin's solution proves that my main point is false: you can create a polyfill for Proxy.
No, you can't. Because Proxys rely on a special (new) behavior of several language syntax elements — namely the . operator and the [index] operator — it cannot be emulated by a polyfill.
The only way do it is to change the syntax that you use. For example, if you wanted to uppercase all string properties via a proxy, you could create a "proxy" object like so:
var object = ...
var proxy = {
get: function proxyGet(key) {
var res = object[key];
if (typeof res === "string") {
res = res.toUpperCase();
}
return res;
}
}
But, then you would still have to call it differently:
proxy.get("myVar");
instead of
object.myVar;
or
proxy.myVar
which is what the new Proxy syntax supports.
Note: You could almost create a polyfill that worked only for methods, by enumerating the function properties of the object, and creating a proxy function on the proxy object for each one of these properties; however, this would not work for non-function properties, since you can't dynamically affect the way they are accessed.
I see a lot of people doing this
Object.prototype.foo = 'HALLO';
var hash = {baz: 'quuz'};
for ( var v in hash ) {
// Do not print property `foo`
if ( hash.hasOwnProperty(v) ) {
console.log( v + " is a hash property" );
}
}
My question is rather than testing .hasOwnProperty each time you wish to use an Object as a hash why not just set the .__proto__ to null on the object? †
hash.prototype = null;
hash.__proto__ = null;
for ( var v in hash ) {
// Do not print property `foo`
console.log( v + " is a hash property" );
}
It has been brought to my attention that __proto__ is nonstandard. That still doesn't answer the question though...
var foo = Object.create(null);
Object.getPrototypeOf(foo);
This can't be an original question, but I can't find anything about changing __proto__ to null to eliminate the drawbacks of having to check for inheritance? What's wrong with this approach, seems to make code faster (don't have to check properties of Object) and cleaner?
† And the .prototype property if you plan on making future children of it.
There's nothing inherently wrong with creating [[Prototype]]'less objects to avoid hasOwnProperty-based checks during "hash" enumeration.
In fact, some of the libraries I know (fuse.js being one of them, IIRC) do exactly that.
Now on to the practical problems:
__proto__ is non-standard. See my compat. table. Notice how IE doesn't support __proto__ up until and including IE9. Of course, IE9 supports Object.create and so it becomes possible to create [[Prototype]]'less object with Object.create(null) but that still leaves IE6, IE7 and IE8. Oh and Opera <10.10, as you can see (which doesn't support Object.create). Fortunately, existence (and functionality) of __proto__ can be easily feature tested, which is why non-supporting browsers can be made to take hasOwnProperty-based or some other route.
Removing [[Prototype]] from the object "removes" all of the Object.prototype.* methods. Naturally. So, for example, myHash.toString() (or even myHash + '') will now error out unless you give that hash toString method. Ditto for valueOf, hasOwnProperty, and all the other Object.prototype.* methods. This isn't big deal, of course, as you can always define those methods (and probably should — to make them specialized for hash usage) but it's more work nevertheless.
As far not finding anything about this approach... I was talking about it at least 2 years ago :) — http://groups.google.com/group/comp.lang.javascript/msg/9451c45080b5e9f0 (likely earlier too, but can't find any other posts on comp.lang.javascript at the moment). There are more interesting findings about browsers' behavior with __proto__ === null in that thread. Check it out.
Not all JS implementations have __proto__ as it is non-standard feature.
I'm seeing posts about a 'new' Object.create that makes enumeration configurable. However, it relies on a Object.defineProperty method. I can't find a cross browser implementation for this method.
Are we stuck writing for the old Object.create? I can't write things that won't work in IE6/7.
There are several things that you can't emulate from the ECMAScript 5 Object.create method on an ECMAScript 3 environment.
As you saw, the properties argument will give you problems since in E3-based implementations there is no way to change the property attributes.
The Object.defineProperty method as #Raynos mentioned, works on IE8, but partially, it can be used only in DOM elements.
Also accessor properties will give you problems, they could be mimicked with widely supported non-standard methods such as __defineGetter__/__defineSetter__, but again, you can't change the property attributes.
Another problem aside the property descriptors, is that the Object.create method can accept null as an argument, to create an object that doesn't inherits from anything.
This can't be emulated with the Crockford's Object.create shim, because when the new operator is used with a constructor function that has a prototype property containing null -or any other non-object value-, the newly created object will inherit from Object.prototype anyway by default.
In some implementations -V8, Spidermonkey, Rhino, etc...- they have a setteable __proto__ property which could be used to set a null [[Prototype]], but again, that's non-standard, and for sure it will never work on IE.
I would recommend, if you want to target old browsers to don't use those features, since there is no way to make them work properly on those environments.
If you still want to use Object.create, without using the properties argument, you could, however I would recommend you to detect the things that can't be emulated.
The following would be a safer version of the Crockford's Object.create shim:
if (typeof Object.create != 'function') {
(function () {
var F = function () {};
Object.create = function (o) {
if (arguments.length > 1) { throw Error('Second argument not supported');}
if (o === null) { throw Error('Cannot set a null [[Prototype]]');}
if (typeof o != 'object') { throw TypeError('Argument must be an object');}
F.prototype = o;
return new F;
};
})();
}
Anyway, use it carefully.
If you want a good defineProperty() implementation, take a look at https://github.com/kriskowal/es5-shim
Unfortunately, you cannot make enumeration configurable in an ES3 environment. This shim will let you call the API in either environment, but the properties will still be enumerable under ES3.
For what it's worth,
Object.defineProperty works in ie8 and FF4.
This means its worthwhile to feature sniff and implement it where it is useful seeing as you would hope the upgrade from ie 6/7 to 8/9 will occur in the next few years.
Another thing to be wary of is that the dontEnum property has a bug in JScript
You will have to work around the way you use the dontEnum property in IE.
[Edit]:
Here's documentation for Internet explorer and a link to the ES5 specification (Page 122 , 15.2.3.6)
ECMAScript 5 has quite a few nice additions. John Resig has a good overview here. Here is a good ECMAScript 5 compatibility table.
A lot of this stuff can be "faked" for browsers that don't support these functions yet. Do you know of any scripts that can do this? I'm particularly interested in Object.create.
For example, Douglas Crockford's JSON script checks if JSON functions exist before creating them.
If there was more like the JSON one we could include them when we need to use the new functions.
Crockford recommends this kind of Object.create shim:
if (typeof Object.create != "function") {
Object.create = function (o) {
function F(){}
F.prototype = o;
return new F;
};
}
But please don't do this.
The problem with this approach is that ES5 Object.create has a signature of 2 arguments: first — an object to inherit from, and second (optional) — an object representing properties (or rather, descriptors) to add to newly created object.
Object.create(O[, Properties]); // see 15.2.3.5, ECMA-262 5th ed.
What we have is an inconsistent implementation with 2 different behaviors. In environments with native Object.create, method knows how to handle second argument; in environments without native Object.create, it doesn't.
What are the practical implications?
Well, if there's some code (say, a third party script) that wants to use Object.create, it's rather reasonable for that code to do this:
if (Object.create) {
var child = Object.create(parent, properties);
}
— essentially assuming that if Object.create exists, it must conform to specs — accept second argument and add corresponding properties to an object.
But, with the above-mentioned shim, second argument is simply ignored. There's not even an indication of something going wrong differently. A silent failure, so to speak — something that's rather painful to detect and fix.
Can we do better?
Well, it's actually impossible to create a fully-conforming Object.create shim using only (standard) ES3 facilities. The best solution is to create a custom wrapper method.
There are, however, few alternative (less than optimal) things you can try:
1) Notify user about inability to work with second argument
if (!Object.create) {
Object.create = function (o) {
if (arguments.length > 1) {
throw Error('second argument is not supported');
}
// ... proceed ...
};
}
2) Try to handle second argument:
if (!Object.create) {
Object.create = function (parent, properties) {
function F(){}
F.prototype = parent;
var obj = new F;
if (properties) {
// ... augment obj ...
}
return obj;
};
}
Note that "properties" is an object representing property descriptors, not just property names/values, and is something that's not very trivial to support (some things are not even possible, such as controlling enumerability of a property):
Object.create(parent, {
foo: {
value: 'bar',
writable: true
},
baz: {
get: function(){ return 'baz getter'; },
set: function(value){ return 'baz setter'; },
enumerable: true
}
});
The other inconsistency in the original shim is that it doesn't take care of parent object being null.
var foo = Object.create(null);
This creates an object whose [[Prototype]] is null; in other words, object that doesn't inherit from anything, not even Object.prototype (which all native objects in ECMAScript inherit from).
foo.toString; // undefined
foo.constructor; // undefined
// etc.
This is, by the way, useful to create "proper" hash tables in ECMAScript.
It's possible to emulate this behavior, but only using non-standard extensions, such as "magical" __proto__ property (so implementation would be not very portable or robust). Solution to this problem is similar: either emulate ES5 implementation fully, or notify about inconsistency/failure.
es5-shim http://github.com/kriskowal/es5-shim/
This was part of the narwhal stand-alone javascript environment, but has been broken out on its own. It's pretty darn mature and precise.
es5 - JavaScript/EcmaScript 5 in 3 is a collection shared at BitBucket. Object.create in particular is an easy one to fake, made popular by Crockford et al, but improved on here by Justin Love, focussing on many ES5 parts.
If you don't mind learning the library and writing some code yourself, you can find some code implementations of the ECMAScript 5 library at
https://developer.mozilla.org/En/JavaScript/ECMAScript_5_support_in_Mozilla
For example, the code for Array.filter
And then Crockford has JSON.parse/stringify in json2.js
https://github.com/douglascrockford/JSON-js