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
Related
It is a pure-Javascript method returning a simple object,
MyClass.retObj = function() {
return {
x: {a:1, b:this.b}
,y: this.y
};
}
document.addEventListener('DOMContentLoaded', function(){ // ONLOAD:
console.log( MyClass.retObj().x ) // good...
console.log( MyClass.retObj() ) // BAD! show __proto__
}, false); // \ONLOAD
Why it returns a __proto__ in a modern browser? How to disable this ugly behaviour in nowadays (2019)?
MDN says that this feature "is no longer recommended".
PS: it is not a duplicate of this question, because here is the simplest and direct context, and I need a simple answer... And it is about "nowadays".
Why [does it show] a __proto__?
The prototype chain is very important if you want to understand why an object behaves in a certain way. Wether it is displayed as __proto__ or [[Prototype]] or <prototype> is not really relevant, is it? ...
How to disable this ugly [object visualization]?
Use another browser if that really bothers you.
curiously MyClass.retObj().x [does not seem to have] __proto__.
Thats indeed fun, probably the console thinks that the Prototype is not relevant to the audience, therefore it hides it, however it isn't really in both cases. On the other hand however this dynamic behaviour of visualizing things is kind of missleading, as one might think that __proto__ does not exist on one object but the other (it does exist on both).
MDN says that [__proto__] "is no longer recommended".
How to disable this ugly [__proto__ property]?
And I wouldn't recommend using var, backwards compability is ugly but mandatory. Removing all those bad design decisions would "break the internet", therefore keeping them is definetly better.
But are there no problem[s]?
Yes, __proto__ is problematic to performance optimizations, but ... it's JavaScript, the browsers know how to optimize the uncertain.
returning an object that is not what I defined?
You also haven't defined {}.toString(). If you really need a completely blank object, use
Object.create(null)
forcing the ingestion of so much rubbish for users of my method?
__proto__ is not enumerable and inherited, you won't find it by accident programmatically if you are not searching for it actively.
No clean JSON?
JSON.stringify({}) // {} ?!?
My answer may sound more opinion than fact, but I'll give it a shot. The referenced article, here, states that the use of __ proto __ is not recommended - that is, avoid putting it directly in your code yourself.
That is, don't do this:
function Person() {
this.Name = "";
}
Person.prototype.setName = function(name) {
this.Name = name;
};
Person.prototype.getName = function() {
return this.Name;
};
var john = new Person();
console.log( john.__proto__ );
Because proto has been recommended to be removed from browsers, you shouldn't use it directly in code as future browser updates may remove it entirely, and then your code will break with those updates.
I have long ago been willing to get the line between native Arrays and regular Objects totally blurred, not only extending Object with the same capabilities as Arrays got in ES5, but bundle up with my custom package of methods on both sides.
Couple of smart people thought about these paradigm changes. Like Angus Croll mentioned it in the article javascript-object-keys-finally:
"Moreover as the line between Arrays and regular Objects blurs (aided
by custom getters and setters) we’re likely to see a growth in generic
“array-like” objects which enjoy the best of both worlds – non-numeric
identifiers and access to the rich API set defined by Array.prototype.
EcmaScript 5 has apparently pre-empted this trend by introducing the
generic method, defined by one type but useable by any."
Along the way, he get things coded in the article:
extending-objects-with-javascript-getters
function extendAsArray(obj) {
if (obj.length === undefined || obj.__lookupGetter__('length')) {
var index = 0;
for (var prop in obj) {
if(!obj.__lookupGetter__(prop)) {
(function(thisIndex, thisProp) {
obj.__defineGetter__(thisIndex, function() {return obj[thisProp]});
})(index, prop)
index++;
}
};
obj.__defineGetter__("length", function() {return index});
}
return obj;
}
var myObj = {
left:50,
top:20,
width:10,
height:10
}
extendAsArray(myObj);
[].map.call(myObj,function(s){return s+' px'}).join(', ');
//"50px ,20px ,10px, 10px"
This approach is exceptionally interesting for me. However, it is also seemingly suffering a couple of serious problems!
How about extending the original myObj model with a couple of new properties?
Should we run extendAsArray on every property change to update it's concerning length property?
When a property changes, it's not just the length property that's relevant;
the array indices should also be updated, because an array-like property request definitely turns out to be undefined. So when
console.log(myObj.length) -> 4
myObj.zAxis=0
then
console.log(myObj[4]) // -> undefined!
console.log(myObj.length) // -> 4!
I have modified Angus' code accordingly, so it supports automatic update of length property on request:
function extendAsArray(obj) {
var index = 0;
for(var prop in obj){
if(!obj.__lookupGetter__(prop)){
(function(thisIndex, thisProp){
Object.defineProperty(obj, thisIndex, {
get: function(){return obj[thisProp]}
, enumerable: true
, configurable: true
, writeable: true
});
})(index, prop)
index++;
}
}
if(!obj.__lookupGetter__('length')){
Object.defineProperty(obj, 'length', {
get: function(){
return extendAsArray(obj);
}
, configurable: true
, writeable: true
});
return obj;
}
else{
return index;
}
}
The problem is: how do we updating the object's array indices together with its length property when a property is changed, added or removed?
Should I use Object.watch?
And there is still an unsolved question: how to interfere with my own unshimmed utility library, having made it also for Objects in a consistent way?
I am using the same codebase for both types: z.Object({}).mapEvery does the same as z.Object([]).mapEvery
Please avoid mentioning JQuery, and Underscore as well. I have got a comprehensive, custom list of methods for both types, and I am willing to use the standards completed possibly with my unshimmed ones, and I am not willing to refactor it!
I guess this is your question:
how do we updating the object's array indices together with its length property when a property is changed, added or removed?
You create methods to do it, so you essentially mimic the Object internal methods. I don't think you can do that with getters and setters, but I may be wrong about that.
The rest is more of a comment than an answer.
I have long ago been willing to get the line between native Arrays and regular Objects totally blurred
The line is already totally blurred. Arrays are Objects, the only thing that sets them apart is their special length property.
EcmaScript 5 has apparently pre-empted this trend by introducing the generic method,
ES5 didn't introduce generic methods, they have been in the language since ed 3 at least.
defined by one type but useable by any
Not at all, in fact ES5 is more restrictive. In ed 3, call and apply coerce the thisArg to an object using Object(*thisArg*), or substitute the global object if nothing was passed. Not so in ES5 which passes thisArg unmodified.
The restriction on using arrays as objects is related to convention, not the language itself. Most developers see a clear divide between when an object or array should be used. There are few cases where you really need to use an array like an object, but no doubt they exist. jQuery is an example of where an Object leverages Array properties, e.g. the elements collected by a selector are added as numeric properties and there is a length property that is the number of elements. That way generic array methods can be applied to jQuery objects (all in ed 3, by the way).
The Object.watch method is in JavaScrpit™, it's not part of ES5 so use with caution.
A major problem with creating your own version of built–in objects is that you'll probably end up wrapping every built–in method in a native one (like jQuery wraps every DOM method pretty much) and start setting getters and setters on every property, or end up with function calls to replace property access (e.g. jQuery's val, attr and prop methods). Rather tedious, and slow if performance matters.
Oh sorry, I mentioned jQuery… :-(
It just seems to be more sensible to design a library or framework to make the best use of the features that the language has, rather than trying to force it to do things it doesn't do well, or does not do natively.
But full marks for trying. :-)
There is a library watch.js out there, which is watching out for either property updates or new property addition as well.
try out!
It is working with setInterval, so it is not performance-friendly however.
When Harmony is out, we can do things that simply:
Object.observe(obj,Observer);
Check the spec for that:
Harmony
But, when later object extension is not in focus, i can freeze up the whole object upon initialization, not to be bothered with neither property changes nor property addition.
The code is changed accordingly:
extendAsArray = function z_extendAsArray(obj){
var index = 0;
for(var prop in obj){
if(!obj.__lookupGetter__(prop)){
(function(thisIndex, thisProp){
Object.defineProperty(obj, thisIndex, {
get: function(){return obj[thisProp]}
, enumerable: true
, configurable: true
, writeable: true
});
})(index, prop)
index++;
}
}
if(!obj.__lookupGetter__('length')){
Object.defineProperty(obj, 'length', {
value:index
});
if(!Object.freeze){
Object.defineProperty(Object, "freeze", {
enumerable: false
, configurable: false
, writable: false
, value: function (obj) {
var props = Object.getOwnPropertyNames(obj);
for(var i=0; i<props.length; i++){
var desc = Object.getOwnPropertyDescriptor(obj,props[i]);
if("value" in desc ){
desc.writable = false;
}
desc.configurable = false;
Object.defineProperty( obj, props[i], desc );
}
return Object.preventExtensions(obj);
}
});
}
Object.freeze(obj);
}
return obj;
};
Also i have found out what Angus Croll, who has been mentioned by the previous post has talked about it.
"Yes, we can make use of the equivalent functionality offered by well written libraries like underscore.js, but still we’re locked into non-standard, inverted signatures in which methods are static and objects are merely extra arguments – an ungainly arrangement for an instance-only language. At some point all supported browsers will be ES5 compliant, at which point the shimmed codebase can simply remove it’s shim library and carry on, while the unshimmed one must choose between a major refactor or a perpetually non-standard and static utility library."
With Crockford's definition:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
and ECMA-262's introduction of Object.create(), we now can set a new object a's hidden prototype property to point to another object b for pure prototypal inheritance. But it is limited to a new object, and Javascript still won't allow something like
a.__proto__ = b;
for an existing object a in the ECMA-262 Javascript specification. Is there a reason to limit it to a new object but not for existing objects?
According to the MDN __proto__ entry, ES6 will allow assigning to an object's [[Prototype]]. The article previously (since October 2010) said that the property was deprecated. It will likely be some years yet before that is useful on the web, particularly as it's very difficult to implement robustly in browsers that don't support it.
I expect that the __proto__ property will be read–only for built–in objects and for host objects that have it.
You'll have to ask Brendan Eich why the [[Prototype]] property was hidden and could only be set through a constructor, but I suspect that he wanted to keep JavaScript simple and not allow the kind of abuse handed out to eval.
As for Crockford's beget, it was first published as clone by Lasse Reichstein Nielsen as a way to clone objects and has since been replaced by ES5 Object.create.
One reason I can see is to avoid the possibility of circular references.
RECAP:
Ok, it's been a while since I asked this question. As usual, I went and augmented the Object.prototype anyway, in spite of all the valid arguments against it given both here and elsewhere on the web. I guess I'm just that kind of stubborn jerk.
I've tried to come up with a conclusive way of preventing the new method from mucking up any expected behaviour, which proved to be a very tough, but informative thing to do. I've learned a great many things about JavaScript. Not in the least that I won't be trying anything as brash as messing with the native prototypes, (except for String.prototype.trim for IE < 9).
In this particular case, I don't use any libs, so conflicts were not my main concern. But having dug a little deeper into possible mishaps when playing around with native prototypes, I'm not likely to try this code in combination with any lib.
By looking into this prototype approach, I've come to a better understanding of the model itself. I was treating prototypes as some form of flexible traditional abstract class, making me cling on to traditional OOP thinking. This viewpoint doesn't really do the prototype model justice. Douglas Crockford wrote about this pitfall, sadly the pink background kept me from reading the full article.
I've decided to update this question in the off chance people who read this are tempted to see for themselves. All I can say to that is: by all means, do. I hope you learn a couple of neat things, as I did, before deciding to abandon this rather silly idea. A simple function might work just as well, or better even, especially in this case. After all, the real beauty of it is, that by adding just 3 lines of code, you can use that very same function to augment specific objects' prototypes all the same.
I know I'm about to ask a question that has been around for quite a while, but: Why is Object.prototype considered to be off limits? It's there, and it can be augmented all the same, like any other prototype. Why, then, shouldn't you take advantage of this. To my mind, as long as you know what you're doing, there's no reason to steer clear of the Object prototype. Take this method for example:
if (!Object.prototype.getProperties)
{
Object.prototype.getProperties = function(f)
{
"use strict";
var i,ret;
f = f || false;
ret = [];
for (i in this)
{
if (this.hasOwnProperty(i))
{
if (f === false && typeof this[i] === 'function')
{
continue;
}
ret.push(i);
}
}
return ret;
};
}
Basically, it's the same old for...in loop you would either keep safe in a function, or write over and over again. I know it will be added to all objects, and since nearly every inheritance chain in JavaScript can be traced back to the Object.prototype, but in my script, I consider it the lesser of two evils.
Perhaps, someone could do a better job at telling me where I'm wrong than this chap, among others. Whilst looking for reasons people gave NOT to touch the Object's prototype, one thing kept cropping up: it breaks the for..in loop-thingy, but then again: many frameworks do, too, not to mention your own inheritance chains. It is therefore bad practice not to include a .hasOwnProperty check when looping through an object's properties, to my mind.
I also found this rather interesting. Again: one comment is quite unequivocal: extending native prototypes is bad practice, but if the V8 people do it, who am I to say they're wrong? I know, that argument doesn't quite stack up.
The point is: I can't really see a problem with the above code. I like it, use it a lot and so far, it hasn't let me down once. I'm even thinking of attaching a couple more functions to the Object prototype. Unless somebody can tell me why I shouldn't, that is.
The fact is, it's fine as long as you know what you're doing and what the costs are. But it's a big "if". Some examples of the costs:
You'll need to do extensive testing with any library you choose to use with an environment that augments Object.prototype, because the overwhelming convention is that a blank object will have no enumerable properties. By adding an enumerable property to Object.prototype, you're making that convention false. E.g., this is quite common:
var obj = {"a": 1, "b": 2};
var name;
for (name in obj) {
console.log(name);
}
...with the overwhelming convention being that only "a" and "b" will show up, not "getProperties".
Anyone working on the code will have to be schooled in the fact that that convention (above) is not being followed.
You can mitigate the above by using Object.defineProperty (and similar) if supported, but beware that even in 2014, browsers like IE8 that don't support it properly remain in significant use (though we can hope that will change quickly now that XP is officially EOL'd). That's because using Object.defineProperty, you can add non-enumerable properties (ones that don't show up in for-in loops) and so you'll have a lot less trouble (at that point, you're primarily worried about name conflicts) — but it only works on systems that correctly implement Object.defineProperty (and a correct implementation cannot be "shimmed").
In your example, I wouldn't add getProperties to Object.prototype; I'd add it to Object and accept the object as an argument, like ES5 does for getPrototypeOf and similar.
Be aware that the Prototype library gets a lot of flak for extending Array.prototype because of how that affects for..in loops. And that's just Arrays (which you shouldn't use for..in on anyway (unless you're using the hasOwnProperty guard and quite probably String(Number(name)) === name as well).
...if the V8 people do it, who am I to say they're wrong?
On V8, you can rely on Object.defineProperty, because V8 is an entirely ES5-compliant engine.
Note that even when the properties are non-enumerable, there are issues. Years ago, Prototype (indirectly) defined a filter function on Array.prototype. And it does what you'd expect: Calls an iterator function and creates a new array based on elements the function chooses. Then ECMAScript5 came along and defined Array.prototype.filter to do much the same thing. But there's the rub: Much the same thing. In particular, the signature of the iterator functions that get called is different (ECMAScript5 includes an argument that Prototype didn't). It could have been much worse than that (and I suspect — but cannot prove — that TC39 were aware of Prototype and intentionally avoided too much conflict with it).
So: If you're going to do it, be aware of the risks and costs. The ugly, edge-case bugs you can run into as a result of trying to use off-the-shelf libraries could really cost you time...
If frameworks and libraries generally did what you are proposing, it would very soon happen that two different frameworks would define two different functionalities as the same method of Object (or Array, Number... or any of the existing object prototypes). It is therefore better to add such new functionality into its own namespace.
For example... imagine, you would have a library that would serialize objects to json and a library that would serialize them to XML and both would define their functionality as
Object.prototype.serialize = function() { ... }
and you would only be able to use the one that was defined later. So it is better if they don't do this, but instead
JSONSerializingLibrary.seralize = function(obj) { ... }
XMLSerializingLibrary.seralize = function(obj) { ... }
It could also happen that a new functionality is defined in a new ECMAscript standard, or added by a browser vendor. So imagine that your browsers would also add a serialize function. That would again cause conflict with libraries that defined the same function. Even if the libraries' functionality was the same as that which is built in to the browser, the interpreted script functions would override the native function which would, in fact, be faster.
See http://www.websanova.com/tutorials/javascript/extending-javascript-the-right-way
Which addresses some, but not all, the objections raised. The objection about different libraries creating clashing methods can be alleviated by raising an exception if a domain specific method is already present in Object.prototype. That will at least provide an alert when this undesirable event happens.
Inspired by this post I developed the following which is also available in the comments of the cited page.
!Object.implement && Object.defineProperty (Object.prototype, 'implement', {
// based on http://www.websanova.com/tutorials/javascript/extending-javascript-the-right-way
value: function (mthd, fnc, cfg) { // adds fnc to prototype under name mthd
if (typeof mthd === 'function') { // find mthd from function source
cfg = fnc, fnc = mthd;
(mthd = (fnc.toString ().match (/^function\s+([a-z$_][\w$]+)/i) || [0, ''])[1]);
}
mthd && !this.prototype[mthd] &&
Object.defineProperty (this.prototype, mthd, {configurable: !!cfg, value: fnc, enumerable: false});
}
});
Object.implement (function forEach (fnc) {
for (var key in this)
this.hasOwnProperty (key) && fnc (this[key], key, this);
});
I have used this primarily to add standard defined function on implementation that do not support them.
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)