Just slides I have showed today here @ nokia for a tech talk, hope you'll enjoy them, cheers
Update
Here you can find the updated version of the Object.forEach proposal: gist 2294934
Main changes are about some inconsistent behavior in Safari, not it should work without problems.
Thanks to @medikoo for his hint.
... rock'n'roll ...
behind the design
My JavaScript book is out!
Don't miss the opportunity to upgrade your beginner or average dev skills.
Showing posts with label ES5. Show all posts
Showing posts with label ES5. Show all posts
Tuesday, April 10, 2012
Friday, March 16, 2012
On Obtrusive Polyfills
I'll try to make the point short and simple, starting with the tl;dr version: if you create a polyfill and during features detection you realize this cannot work, drop it and let other libs deal with it.
It was the case of Object.defineProperty shimmed through es5-shim code ... this library is good but that method, as many others, are simply broken.
I was trying to add a setter/getter in IE < 9 and shit happened, more specifically a thrown exception happened ... the repository itself explains that this method and others should fail silently ... so it's OK for the developer to know about it and avoid trusting these methods ...

I simply trust the fact the browser, or some library, fixed the problem for me and I can use this method as it is.
A broken shim doesn't make anyone life easy while that simple condition in my code could redirect my logic in a completely different approach.
Since in IE < 9 that method simply does not make sense to exist, then it should not exist, period.
I would not compromise entirely theObject.create but again, if defineProperty() is not there there is no bloody point on using create() too.
Let other libraries deal with the fact this is missing in their own way since every single case could be different: someone simply need to inherit another object, someone else might need the method as it should be.
Thanks for understanding
Broken Object.defineProperty
This is just one case really annoying. Bear in mind I am not talking about "making perfect polyfills", this is almost impossible most of the case, I am talking about broken runtime even if you did not really do anything that special.It was the case of Object.defineProperty shimmed through es5-shim code ... this library is good but that method, as many others, are simply broken.
I was trying to add a setter/getter in IE < 9 and shit happened, more specifically a thrown exception happened ... the repository itself explains that this method and others should fail silently ... so it's OK for the developer to know about it and avoid trusting these methods ...
This Is Not OK
If I check if Object.defineProperty is there and this is all I need to know, I don't want to double check which version of IE, if IE, it is, neither I want to check anything extra.I simply trust the fact the browser, or some library, fixed the problem for me and I can use this method as it is.
if ("defineProperty" in Object) {
// I want to use it, wouldn't check for it otherwise
}
A broken shim doesn't make anyone life easy while that simple condition in my code could redirect my logic in a completely different approach.
Since in IE < 9 that method simply does not make sense to exist, then it should not exist, period.
Remove The Broken Shim
I hope, and expect, that es5-guys will simply delete Object.defineProperty afterward, once realized it's not usable.I would not compromise entirely theObject.create but again, if defineProperty() is not there there is no bloody point on using create() too.
Let other libraries deal with the fact this is missing in their own way since every single case could be different: someone simply need to inherit another object, someone else might need the method as it should be.
As Summary
A shim/polyfill that works 90% is OK, specially if the API is covered 100% and even if behind the scene things are not identical, but a polyfill that let a piece of code think everything is fine and the whole app breaks who knows when and why should not be exposed at all or, if you really want to bring similar functionality, it should be exposed prefixed, i.e. Object.$defineProperty() so that the rest of the code is aware that such method exists but it's limited.Thanks for understanding
Saturday, August 20, 2011
Overloading the in operator
In all its "sillyness", the CoffeeShit project gave me a hint about the possibilities of an overloaded in operator.
However, I bet at least once in our JS programming life we have done something like this, expecting a true rather than false.
This is indeed different from an Identifier, where in latter ReservedWord is not allowed.
"... bla, bla bla ..." ... right, the human friendly version of what I've just said is that obj.in, obj.class, obj.for etc etc are all accepted identifiers names.
Accordingly, to understand if the current browser is ES5 specs compliant we could do something like this:
Details on bad usage of new a part, this is actually how I would use this method to boost up performances.
cool?
However, since in ES5 we have new possibilities, I would say that if everybody agrees on some specific case ... why not?
for/in loops can now be safe and some ReservedWord can be the most semantic name to represent a procedure as demonstrated in this post.
Another quick example?
I can already see Prototype3000 farmework coming out with all these magic tricks in place :D
Have fun with ES5 ;)
The Cross Language Ambiguity
In JavaScript, the in operator checks if a property is present where the property is the name rather than its value.
"name" in {name:"WebReflection"}; // true
However, I bet at least once in our JS programming life we have done something like this, expecting a true rather than false.
4 in [3, 4, 5]; // false
// Array [3, 4, 5] has no *index* 4
The Python Way
In Python, as example, last operation is perfectly valid!The behavior of Python in operator is indeed more friendly in certain situations.
"b" in ("a", "b", "c") #True
4 in [3, 4, 5] # True
value in VS value.in()
What if we pollute the Object.prototype with an in method that is not enumerable and sealed? No for/in loops problems, neither cross browsers issues since if it's possible in our target environment, we do it, otherwise we don't do it ... a fair compromise?Rules
- if the target object is an Array, check if value is contained in the Array ( equivalent of -1 < target.indexOf(value) )
- if the target object is an Object, check if value is contained in one of its properties ( equivalent of for/in { if(object[key] === value) return true; } ).
I don't think nested objects should be checked as well and right now these are not ( same as native Array#indexOf ... if it's an array of arrays internal arrays values are ignored and that's how it should be for consistency reason ) - if the target object is a typeof "string", check if value is a subset of the target ( equivalent of -1 < target.indexOf(value) ).
The Python logic on empty string is preserved since in JavaScript "whateverStringEvenEmpty".indexOf("") is always 0 - if the target property is a typeof "number", check if value is a divisor of the target ( as example, (3).in(15) === true since 15 can be divided by 3)
Compatibility
The "curious fact" is that in ES5 there is no restrictions on an IdentifierName.This is indeed different from an Identifier, where in latter ReservedWord is not allowed.
"... bla, bla bla ..." ... right, the human friendly version of what I've just said is that obj.in, obj.class, obj.for etc etc are all accepted identifiers names.
Accordingly, to understand if the current browser is ES5 specs compliant we could do something like this:
Back in topic, IE9 and updated Chrome, Firefox, Webkit, or Safari are all compatible with this syntax.
try {
var ES5 = !!Function("[].try");
} catch(ES5) {
ES5 = !ES5;
}
alert(ES5); // true or false
New Possibilities
Do you like Ruby syntax?
// Ruby like instances creation (safe version)
Function.prototype.new = function (
anonymous, // recycled function
instance, // created instance
result // did you know if you
// use "new function"
// and "function" returns
// an object the created
// instance is lost
// and RAM/CPU polluted
// for no reason?
// don't "new" if not necessary!
) {
return function factory() {
// assign prototype
anonymous.prototype = this.prototype;
// create the instance inheriting prototype
instance = new anonymous;
// call the constructor
result = this.apply(instance, arguments);
// if constructor returned an object
return typeof result == "object" ?
// return it or return instance
// if result is null
result || instance
:
// return instance
// in all other cases
instance
;
};
}(function(){});
// example
function Person(name) {
this.name = name;
}
var me = Person.new("WebReflection");
alert([
me instanceof Person, // true
me.name === "WebReflection" // true
].join("\n"));
Details on bad usage of new a part, this is actually how I would use this method to boost up performances.
// Ruby like instances creation (fast version)
Function.prototype.new = function (anonymous, instance) {
return function factory() {
anonymous.prototype = this.prototype;
instance = new anonymous;
this.apply(instance, arguments);
return instance;
};
}(function(){});
cool?
Do Not Pollute Native Prototypes
It does not matter how cool and "how much it makes sense", it is always considered a bad practices to pollute global, native, constructors prototypes.However, since in ES5 we have new possibilities, I would say that if everybody agrees on some specific case ... why not?
for/in loops can now be safe and some ReservedWord can be the most semantic name to represent a procedure as demonstrated in this post.
Another quick example?
// after Function.prototype.new
// after Person function declaration
Object.defineProperty(Object.prototype, "class", {
enumerable: !1,
configurable: !1,
get: function () {
return this.__proto__.constructor;
}
});
var me = Person.new("WebReflection");
// create instance out of an instance
var you = me.class.new("Developer");
alert([
you instanceof Person, // true
you.name === "Developer" // true
].join("\n"));
I can already see Prototype3000 farmework coming out with all these magic tricks in place :D
Have fun with ES5 ;)
Sunday, June 05, 2011
ES5 and use strict
Update
There was another article about it which has less examples but more complementary points or descriptions.
Moreover, that page links to a specific use strict compatibility page, right now green only with Firefox Aurora and Google Chrome Canary.
However, another page shows more use strict cases as well and Canary seems to miss one check while Webkit Nightly shows all green YES. Opera Next is not there yet while IE10 surprisingly scores all of them except Function declaration in statements.
Well done guys!
on page 233 of the ECMAScript 5th Edition we can read details about "use strict" activation and what it means for our code.
Somebody believes this ES5 feature can help developers to write less errors ... well, I think not everything is that good as it looks.
This post is about all those points with concrete examples.
Base 8 has not many use cases on daily JS tasks. As example, to obtain the number 8 in base 8 we can write something like 010 where:
The problem is that 01 is not enough to force the engine to consider that number a base 8 and it will be interpreted as 1 indeed. Fair enough, I personally don't give a hack about base 8 so this does not hurt, it's just slightly simplified numbers parsing.
Same story for OctalEscapeSequence, no "octal magic" anymore.
Above example is a classic one ... or better, a classic for newcomers with PHP or Python background, as example, where local variables are implicit and global one should be explicit.
While Python has a sort of implicit scope lookup with classes, PHP introduced closures only recently (well, 5.3 which should be right now the default minor version in every bloody server).
Since after 1 day of JavaScript development you should have got it (use var for local scope variables) I do believe this is more a limit, rather than a feature.
First of all, the current situation is quite naif since FireFox does not throw anything, it just stop working, while other browsers may nicely ignore this "feature".
Moreover, the moment we want to define a global reference we need to have in our closure a safe reference to the global context or we are simply screwed.
The second part of this point is that if the reference has been defined as writable:false, aka read only as undefined is, a TypeError should be thrown.
Funny enough, if we have nested scope that relies in undefined but the outer one has something like:
nothing will happen, undefined is still not trustable.
Fine for eval, but a kinda common arguments trick won't be usable anymore.
They call it "graceful migration", I call it WTF. If arguments is still there and it's a bloody object similar to an Array, why this object should throw an error with an undefined property?
OK, it's about migration, but actually what use strict introduced here is caller and callee as new reserved words, at least for properties of arguments or whatever function ... well done ...
To be honest, whenever it helps or not, I wonder who the hell ever used the first dynamic shared arguments indexed property value case on any sort of logic code ... was it an ES3 gotcha? Well, in such case I agree, it does not make fucking sense so ... thanks, I am sure somebody in this world will have problems about this new entry.
However, somebody that does not know JavaScript and programming principles as well may have problems ( nothing personal man, you just did it wrong 'till now ).
Sarcasm a part, it's good to have this clarification on specs.
Now, since properties order is not granted at all even in a classic for(var key in obj){} loop, this point is about being not ambiguous and do the right assignment once. As summary, with use strict above example will produce an error: property name "one" appears more than once in object literal.
Once again, if you ever tried to assign same property more than once ... well, I can just say it's good they made this less ambiguous but I do believe this won't improve anybody code quality (being a mistake every Unit Test would have spot in any case).
All cases will throw an error so, once again, hwew ES5 is introducing partial reserved keywords and this is wrong, imho.
Actually, it's not about this as undefined at all, it's about not changing the reference to something different.
There is a classic trick to obtain the global object in the most secure possible way:
Above example is the equivalent of this function:
Untill now, the this reference has always been changed into the global object if the context was null or undefined.
Moreover, the reference has been changed into a proper object reference with primitives values such boolean, number, and string.
I don't remember I have ever defined properties runtime when the callback was about primitives values.
To me is like using objects as trash bin since nothing can be possibly reused after the function has been invoked.
This is what is changed in ES5 and use strict, there is no magic anymore when call or apply are used.
A primitive value will be primitive, an undefined one will be undefined and null will be null.
Here the test case:
It must be said that all these changes make life easier for engines behind the language since call and apply are widely used and all those checks about the context type and its eventual convertion are gone.
At the same time I would have reserved null as only exception to retrieve the global context since we have no more any safe way to do it and this is in my opinion bad.
We cannot do this kind of mistake anymore since a SyntaxError will occur.
The fact this was not possible was clear in ES3 specs but ,,, hey, now we know it better.
Only object properties with a configurable option equal to true can be deleted and nothing else.
To seal/froze the property and to avoid delete operation all we need to do is to mark it as not configurable.
Since to be able to set properties as non configurable we need ES5 already, I think this was a mistake in the use strict rules because I would expect the same behavior for something introduced in ES5 as well as Object.defineProperty is.
I seriously do not want to spend more than I have done already about it so ... forget it, be happy about the choice and shut up or Mr Crockford and all minifiers will come out the dark wardrobe and punch you in the face.
These cannot be used in function expressions, declarations, as variables, these cannot be reassigned, these must be used exclusively for what these are ... got it?
I am not happy about many choices, specially regarding the caller property which was a must have for debugging and introspective purpose but ... hey, engines are not clever enough to activate these things when necessary but these engines are able to swap runtime a totally different behavior between a non strict function and a strict one.
In few words we still do not have full JavaScript potential here because of this transition that is apparently revolutionary behind the scene, surely not the best present ever for all developers that got JavaScript and used few tricks when necessary to improve their application logic and, why not, security.
Well ... deal with "use strict" and put it there by default or shut up for all new version of JavaScript ... this is the way in any case.
There was another article about it which has less examples but more complementary points or descriptions.
Moreover, that page links to a specific use strict compatibility page, right now green only with Firefox Aurora and Google Chrome Canary.
However, another page shows more use strict cases as well and Canary seems to miss one check while Webkit Nightly shows all green YES. Opera Next is not there yet while IE10 surprisingly scores all of them except Function declaration in statements.
Well done guys!
on page 233 of the ECMAScript 5th Edition we can read details about "use strict" activation and what it means for our code.
Somebody believes this ES5 feature can help developers to write less errors ... well, I think not everything is that good as it looks.
This post is about all those points with concrete examples.
No OctalIntegerLiteral or OctalEscapeSequence
Base 8 has not many use cases on daily JS tasks. As example, to obtain the number 8 in base 8 we can write something like 010 where:
alert(010 === 8);
The problem is that 01 is not enough to force the engine to consider that number a base 8 and it will be interpreted as 1 indeed. Fair enough, I personally don't give a hack about base 8 so this does not hurt, it's just slightly simplified numbers parsing.
Same story for OctalEscapeSequence, no "octal magic" anymore.
No Global Variables
If our closure has a reference not defined in the outer scope, there is no global variable but a ReferenceError.
(function(){"use strict";
for(i = 0; i < 2; i++) {
// never executed due "i" ReferenceError
}
}());
Above example is a classic one ... or better, a classic for newcomers with PHP or Python background, as example, where local variables are implicit and global one should be explicit.
While Python has a sort of implicit scope lookup with classes, PHP introduced closures only recently (well, 5.3 which should be right now the default minor version in every bloody server).
Since after 1 day of JavaScript development you should have got it (use var for local scope variables) I do believe this is more a limit, rather than a feature.
First of all, the current situation is quite naif since FireFox does not throw anything, it just stop working, while other browsers may nicely ignore this "feature".
Moreover, the moment we want to define a global reference we need to have in our closure a safe reference to the global context or we are simply screwed.
The second part of this point is that if the reference has been defined as writable:false, aka read only as undefined is, a TypeError should be thrown.
(function(){"use strict";
undefined = 123;
// throw TypeError
}());
Funny enough, if we have nested scope that relies in undefined but the outer one has something like:
(function(){"use strict";
var undefined = 123;
// other nested functions
}());
nothing will happen, undefined is still not trustable.
Safer arguments and eval
... but wasn't eval evil? Anyway, in the forth point of use strict specifications we have errors whenever we try to reassign arguments or eval.Fine for eval, but a kinda common arguments trick won't be usable anymore.
// this will not work anymore
(function (context){"use strict";
arguments = [].slice.call(arguments, 1);
// some operation with arguments as Array
outerCallback.call(context, arguments);
}());
Goodbye arguments caller and callee
Once again, arguments.callee is gone. Moreover, arguments.callee.caller is done as well but in this case it's not about the caller property, the whole callee concept is gone.
(function anonymous(){"use strict";
alert(anonymous.caller); // throws TypeError
arguments.callee; // throws TypeError
}());
They call it "graceful migration", I call it WTF. If arguments is still there and it's a bloody object similar to an Array, why this object should throw an error with an undefined property?
OK, it's about migration, but actually what use strict introduced here is caller and callee as new reserved words, at least for properties of arguments or whatever function ... well done ...
arguments indexes
Whenever you have noticed or not, if you change a named argument value the arguments object will be affected at the same time. Here a basic example:
// before
(function (a, b){
var c = a;
a = b;
b = c;
alert([].slice.call(arguments));
// b, a
}("a", "b"));
// after
(function (a, b){"use strict";
var c = a;
a = b;
b = c;
alert([].slice.call(arguments));
// a, b
}("a", "b"));
To be honest, whenever it helps or not, I wonder who the hell ever used the first dynamic shared arguments indexed property value case on any sort of logic code ... was it an ES3 gotcha? Well, in such case I agree, it does not make fucking sense so ... thanks, I am sure somebody in this world will have problems about this new entry.
However, somebody that does not know JavaScript and programming principles as well may have problems ( nothing personal man, you just did it wrong 'till now ).
Sarcasm a part, it's good to have this clarification on specs.
Bindings and arguments
For strict mode functions, if an arguments object is created the binding of the local identifier arguments to the arguments object is immutable and hence may not be the target of an assignment expression. (10.5).Well, if you get anything different form what I have said already about redefining the arguments reference/object, please do not hesitate to wake me up in the middle of the night while I am on vacations since seriously I cannot figure out what's this point about.
Unique Object Property Name
With ES3 we could have done something like:
(function (){
var o = {
one: 1,
one: 2
};
alert(o.one); // 2 ???
}());
Now, since properties order is not granted at all even in a classic for(var key in obj){} loop, this point is about being not ambiguous and do the right assignment once. As summary, with use strict above example will produce an error: property name "one" appears more than once in object literal.
Once again, if you ever tried to assign same property more than once ... well, I can just say it's good they made this less ambiguous but I do believe this won't improve anybody code quality (being a mistake every Unit Test would have spot in any case).
arguments and eval as reserved identifiers
It's just like that, an argument cannot be called eval or arguments otherwise we gonna have a SyntaxError.
(function (){"use strict";
function testEval(eval){}
function testArguments(arguments){}
var o = {
get test(eval) {}
set test(eval) {}
};
}());
All cases will throw an error so, once again, hwew ES5 is introducing partial reserved keywords and this is wrong, imho.
Strict eval
I am not sure I got the next point, but here some behavior:Apparently eval has been maden a bit safer but I can see its evil nature all over the place without problems. Kinda good that function defined through eval inside a strict function are automatically strict as well so I guess this point is about strict inheritance through evaluation.
var evil = (function anonymous(){"use strict";
return function (o_O) {
var result = eval(o_O);
alert(b);
return result;
};
}());
evil("function b(){}");
// function b(){'use strict';}
// example 2
var evil = (function anonymous(){"use strict";
var a = 123;
return function (o_O) {
var result = eval(o_O);
alert(b());
return result;
};
}());
evil("function b(){return a}"); // 123
Strict this
There are different behaviors completely changed and it's not all about undefined === this.Actually, it's not about this as undefined at all, it's about not changing the reference to something different.
There is a classic trick to obtain the global object in the most secure possible way:
var global = function(){return this}();
alert(global); // [object Window]
// [object global] in node.js
Above example is the equivalent of this function:
function Global() {
return this;
}
window == Global.call() == Global.call(null) == Global.call(undefined);
// true
Untill now, the this reference has always been changed into the global object if the context was null or undefined.
Moreover, the reference has been changed into a proper object reference with primitives values such boolean, number, and string.
// ES3
function previousHello() {
// primitive converted into new Primitive
// e.g. this reference is a new String(s)
alert("Hello " + this); // Hello World
// we can add properties to new String
this.test = 123;
alert(this.test); // 123
}
var s = "World";
previousHello.call("World");
alert(s.test); // undefined
// since properties cannot be attached to a primitive
I don't remember I have ever defined properties runtime when the callback was about primitives values.
To me is like using objects as trash bin since nothing can be possibly reused after the function has been invoked.
This is what is changed in ES5 and use strict, there is no magic anymore when call or apply are used.
A primitive value will be primitive, an undefined one will be undefined and null will be null.
Here the test case:
// ES5
function sayHello() {"use strict";
alert("Hello " + this); // Hello World
this.test = 123;
alert(this.test); // undefined
}
sayHello.call("World");
function nullThis() {"use strict";
alert(this); // null
}
nullThis.call(null);
function undefinedThis() {"use strict";
alert(this); // undefined
}
undefinedThis.call();
// same as
undefinedThis.call(undefined);
It must be said that all these changes make life easier for engines behind the language since call and apply are widely used and all those checks about the context type and its eventual convertion are gone.
At the same time I would have reserved null as only exception to retrieve the global context since we have no more any safe way to do it and this is in my opinion bad.
More greedy delete
While before we could have tried to delete variables, and without success:
(function test(a){
var b = "b";
delete a;
delete b;
delete test;
alert([a, b, test]);
// a,b,function test(){...}
}("a"));
We cannot do this kind of mistake anymore since a SyntaxError will occur.
(function test(a){"use strict";
var b = "b";
delete a;
delete b;
delete test;
alert([a, b, test]);
}("a"));
// SyntaxError
// applying the 'delete' operator to an unqualified name
The fact this was not possible was clear in ES3 specs but ,,, hey, now we know it better.
Only object properties with a configurable option equal to true can be deleted and nothing else.
TypeError on delete
Even if a property is not writable, we can still delete it since it is considered a configuration option.
(function test(a){"use strict";
var o = Object.create(null, {
deletable: {
value: 123,
configurable: true,
writable: false,
enumerable: true
}
});
alert(o.deletable); // 123
// o.deletable = 456; // Error: read-only
// bye bye property
delete o.deletable;
alert(o.deletable); // undefined
// free to manipulate
o.deletable = 456;
alert(o.deletable); // 456
}());
To seal/froze the property and to avoid delete operation all we need to do is to mark it as not configurable.
(function test(a){"use strict";
var o = Object.create(null, {
deletable: {
value: 123,
configurable: false,
writable: false,
enumerable: true
}
});
delete o.deletable;
// Error: property o.deletable is non-configurable
// and can't be deleted
}());
Since to be able to set properties as non configurable we need ES5 already, I think this was a mistake in the use strict rules because I would expect the same behavior for something introduced in ES5 as well as Object.defineProperty is.
Goodbye with statement
Whenever you like it or not, the with statement is gone.I seriously do not want to spend more than I have done already about it so ... forget it, be happy about the choice and shut up or Mr Crockford and all minifiers will come out the dark wardrobe and punch you in the face.
Reserved arguments and eval identifiers
Everything else about use strict is related to arguments and eval keywords.These cannot be used in function expressions, declarations, as variables, these cannot be reassigned, these must be used exclusively for what these are ... got it?
Summary
ES5 introduced use strict to let developers be familiar with things that will disappear soon in the next version of JavaScript.I am not happy about many choices, specially regarding the caller property which was a must have for debugging and introspective purpose but ... hey, engines are not clever enough to activate these things when necessary but these engines are able to swap runtime a totally different behavior between a non strict function and a strict one.
In few words we still do not have full JavaScript potential here because of this transition that is apparently revolutionary behind the scene, surely not the best present ever for all developers that got JavaScript and used few tricks when necessary to improve their application logic and, why not, security.
Well ... deal with "use strict" and put it there by default or shut up for all new version of JavaScript ... this is the way in any case.
Wednesday, March 16, 2011
Object.defineHybridProperty
Update Yes, I did it: getters and setters for IE < 9 and other browsers
After my early Hooorrayyyy! about compatible IE < 9 getters and setters, I have been experimenting a bit more on how to solve the JSObject to VBVariant and vice-versa assignment and the result was an horrendous monster loads of potential memory leaks and performances implications for the already slow bounce of browsers such IE8, 7, and 6.
Since limitations were also too many, as described in this even earlier attempt from dojo framework, I have realized that VBScript was simply a no-go, or better, probably the wrong answer to the question: how can I have cross-browser getters and setters?
As showed above, the jQuery().html method can be considered a friendly answer for a cross browser get/set implementation, and surely much more friendly than what antimatter15 proposed some time ago (Pssss! dude, I could not find your real name in the "About" section ...).
However, even using these fallbacks, the descriptor object will result inconsistent because writable, enumerable, and configurable properties won't act as expected.
At this point I have decided to fallback into a "jQuery approach" solution that will behave exactly the same in all old and newer browsers, being still able to use an ES5 like descriptor that in a not that far away future won't even need to be changed at all, it will simply work.
The source code of Object.defineHybridProperty, together with Object.defineHybridProperties, is here and as you can see it's a quite simple and compact piece of code.
Hybrid properties could be confused with methods ... and actually, all hybrid properties that have a get and/or set descriptor, become de-facto an object methods with current constrain: zero arguments to get, 1 single argument to set.
Use cases have been already described, a jQuery like API could use without problems the current approach, making the future ES5 only refactory less painful than whatever other get/set approach.
Indeed, once we know which descriptor is using getters/setters, all we have to do is to remember which property has been made hybrid, and change accordingly each invoke with arguments as assignment, and removing brackets from every other empty invoke.
Please Note I will update later the code in order to properly assign Object.prototype native names such toString so that IE browsers will consider them as well.
Done, it's updated and 409 bytes minified and gzipped, have fun :)
After my early Hooorrayyyy! about compatible IE < 9 getters and setters, I have been experimenting a bit more on how to solve the JSObject to VBVariant and vice-versa assignment and the result was an horrendous monster loads of potential memory leaks and performances implications for the already slow bounce of browsers such IE8, 7, and 6.
Since limitations were also too many, as described in this even earlier attempt from dojo framework, I have realized that VBScript was simply a no-go, or better, probably the wrong answer to the question: how can I have cross-browser getters and setters?
The jQuery Hybrid Answer
Back in 2009, James Padolsey described the jQuery framework as a sort of getters/setters simulator API, comparing the semantic and beauty of non-standard Spidermonkey __defineGetter__ and __defineSetter__, against jQuery coding style, where many "methods" could be considered as getters or setters.
// get the innerHTML of the first node found through the selector
$("#selector").html();
// set the innerHTML of the first(?) node found through the selector
$("#selector").html("");
As showed above, the jQuery().html method can be considered a friendly answer for a cross browser get/set implementation, and surely much more friendly than what antimatter15 proposed some time ago (Pssss! dude, I could not find your real name in the "About" section ...).
My "due jQuery success, why not!" Proposal
In ES5 everything is so natural and simple, Object.defineProperty and Object.defineProperties work like a charm and IE9 with all other browsers as well. We may decide to use good old __defineGetter/Setter__ as fallback for older Opera, Chrome, Safari, or Firefox, but not for IE since these methods are not supported at all.However, even using these fallbacks, the descriptor object will result inconsistent because writable, enumerable, and configurable properties won't act as expected.
At this point I have decided to fallback into a "jQuery approach" solution that will behave exactly the same in all old and newer browsers, being still able to use an ES5 like descriptor that in a not that far away future won't even need to be changed at all, it will simply work.
A generic Person.prototype Descriptor
var personDescriptor = {
// requires council notification on change
name: {
get: function () {
return this._name;
},
set: function (name) {
// notify here the council about this change
this._name = name;
}
},
// this property is unfortunately immutable (via public access)
age: {
get: function () {
return this._age;
}
},
// simply a method that occurs once per year
birthday: {
value: function () {
this._age++;
}
}
};
Current ES5 Person "Class" Example
//* ES5 example
function Person(name, age) {
// a new Person in town
this._age = age || 0; // default, just born
this.name = name;
}
Object.defineProperties(Person.prototype, personDescriptor);
var me = new Person("Andrea", 32);
// will throw an error
// me.age = 20;
alert([me.name, me.age]); // Andrea, 32
me.birthday();
alert(me.age); // 33
//*/
Current ES3 Person "Class" Example
//* ES3 example
Person = function Person(name, age) {
// a new Person in town
this._age = age || 0; // default, just born
// we still want to notify the council,
// no direct this._name set
this.name(name);
}
Object.defineHybridProperties(Person.prototype, personDescriptor);
var me = new Person("Andrea", 32);
// nothing will happen
// me.age(20);
alert([me.name(), me.age()]); // Andrea, 32
me.birthday();
alert(me.age()); // 33
// wanna know what they are?
alert(me.name);
alert(me.age);
alert(me.birthday);
//*/
The source code of Object.defineHybridProperty, together with Object.defineHybridProperties, is here and as you can see it's a quite simple and compact piece of code.
Use Cases and DONTS
The function name should be explicit enough, and it's used to define hybrid properties.Hybrid properties could be confused with methods ... and actually, all hybrid properties that have a get and/or set descriptor, become de-facto an object methods with current constrain: zero arguments to get, 1 single argument to set.
Use cases have been already described, a jQuery like API could use without problems the current approach, making the future ES5 only refactory less painful than whatever other get/set approach.
Indeed, once we know which descriptor is using getters/setters, all we have to do is to remember which property has been made hybrid, and change accordingly each invoke with arguments as assignment, and removing brackets from every other empty invoke.
Done, it's updated and 409 bytes minified and gzipped, have fun :)
Tuesday, January 25, 2011
ES6, Harmony, and JS++
... where JS++ is simply metaphoric, in the meaning of "empowered JS" :)
Here the slides I have showed last friday during another team meeting, enjoy!
Here the slides I have showed last friday during another team meeting, enjoy!
Saturday, January 30, 2010
[ES5] Classes As Descriptor Objects
In my latest posts I have talked about current situation for JavaScript "Classes". In Good Old And Common JS Errors I have introduced a misconception of a generic "Class" function which is usually not able to produce instanceof Class, considering Classes in JS are functions indeed.
In Better JS Classes I have explained how to write a meaningful Class factory, avoiding the _super pollution over each extended method, while in the JS _super Bullshit I have explained why the _super/parent concept does not scale with JavaScript prototypal inheritance model.
More than a dev, Mr Crockford included, agreed that in JavaScript the classical pattern does not fit/scale/produce expected results, and even worst, it could cause "disasters" during a session and slow down overall performances (and about this topic I have already said that web developers should stop to test their stuff with powerful CPU, read Macs!
Buy a bloody Atom based device as I have done and after that we can talk about the meaning of performances, right? Netbook should be able to surf without 100% of CPU usage, do you agree?)
The best part over all these years of wrong classical inheritance implementation is that with JavaScript.Next, aka ES5, the way we can define instances does not even require a function definition. Wanna know more?
Pretty cool, isn't it, but I am sure somebody is already arguing something like: "... and what about instanceof?"
A common mistake is to think that instanceof is related to the function itself while it has nothing to do with it.
Is it clear? instanceof works only with functions and only with those functions with an implicit prototype property, the default one, or specified as a generic object.
To avoid above error, being JavaScript 99% dynamic, we could use a safer check, and for this example via isPrototypeOf:
Boring, slow, etc etc ... why don't we use directly our initial class description to understand if an instance is inheriting that class?
As alternative, we could use the global Object.getPrototypeOf method:
To be honest, for both easier scope resolution and semantic, I prefer the isPrototypeOf way. Furthermore, specially if the chain is deeper than 1 level, getPrototypeOf could generate false negatives while getPrototypeOf won't.
Accordingly, the best function to emulate an instanceOf function over objects or "classes" could be:
The only place where all those checks could make sense is inside Object.defineProperty, and not twice in both defineProperties AND definePrperty, since if latter exists, why on earth we should try to emulate its internal checks?
If defineProperty does NOT exists, why shoud we try to emulate it without checks? I hope MDC guys will remove that nonsense from that page, I'd love to be sure developers get ES5 properly, good practices included.
Back in the topic, here there is an example:
Does it make sense? We have more power via defineProperties and we can reuse, or share, objects across the whole library without any kind of problem.
One thing we should be ware about, is that A.prototype is always re-assignable, so if we try to define the A prototype property itself as non configurable we won't be safer anyway, it can be overwritten!
Unfortunately, and as usual, Internet Explorer is behind every other browser and some method cannot be perfectly emulated.
This is also why I have decided to show both defineProperties and create way, since IE defineProperties works only with DOM prototypes and global Window (or generally speaking only with natives and not with object) so that create could be used without problems, so far avoiding get/set and considering that configuration properties may not be respected.
I know this is crap, but until IE9 or the day we'll finally decide to drop this browser support, there's not that much we can do: annoying!
P.S. to test all these natively, we can use a WebKit nightly build.
In Better JS Classes I have explained how to write a meaningful Class factory, avoiding the _super pollution over each extended method, while in the JS _super Bullshit I have explained why the _super/parent concept does not scale with JavaScript prototypal inheritance model.
More than a dev, Mr Crockford included, agreed that in JavaScript the classical pattern does not fit/scale/produce expected results, and even worst, it could cause "disasters" during a session and slow down overall performances (and about this topic I have already said that web developers should stop to test their stuff with powerful CPU, read Macs!
Buy a bloody Atom based device as I have done and after that we can talk about the meaning of performances, right? Netbook should be able to surf without 100% of CPU usage, do you agree?)
The best part over all these years of wrong classical inheritance implementation is that with JavaScript.Next, aka ES5, the way we can define instances does not even require a function definition. Wanna know more?
Objects Extend Objects
It's that simple, we all know this is what happens in OOP Languages based over prototypal inheritance ... and since it is that simple ...ES5 Classes As Descriptors
Or if you prefer, as definition objects. Yep! While every framework is using functions to create instances via new, in ES5 we could completely forget this pattern.
// class descriptor/definition
var MyClass = {
getValue:function () {
return this._value;
},
setValue:function (_value) {
this._value = _value;
}
};
// MyClass instance
var mc = Object.create(
// inherits from MyClass
MyClass,
// define privileged properties or methods
{
_value:{
value:"Hello ES5"
}
}
);
// Hello ES5
alert(mc.getValue());
Pretty cool, isn't it, but I am sure somebody is already arguing something like: "... and what about instanceof?"
instanceof
The instanceof operator checks if an object inherits from the implicit constructor prototype.A common mistake is to think that instanceof is related to the function itself while it has nothing to do with it.
function A() {};
var klass = {};
A.prototype = klass;
var a = new A;
// true
alert(a instanceof A);
A.prototype = {};
// false
alert(a instanceof A);
function B(){};
B.prototype = klass;
// true
alert(a instanceof B);
Is it clear? instanceof works only with functions and only with those functions with an implicit prototype property, the default one, or specified as a generic object.
function A() {};
A.prototype = null;
var a = new A;
// throws: 'prototype' property of A is not an object
alert(a instanceof A);
To avoid above error, being JavaScript 99% dynamic, we could use a safer check, and for this example via isPrototypeOf:
function instanceOf(o, F) {
return !!F.prototype && F.prototype.isPrototypeOf(o);
};
function A() {};
var a = new A;
// true
alert(instanceOf(a, A));
Boring, slow, etc etc ... why don't we use directly our initial class description to understand if an instance is inheriting that class?
// using first example code
alert(MyClass.isPrototypeOf(mc));
// true
As alternative, we could use the global Object.getPrototypeOf method:
Object.getPrototypeOf(mc) === MyClass;
To be honest, for both easier scope resolution and semantic, I prefer the isPrototypeOf way. Furthermore, specially if the chain is deeper than 1 level, getPrototypeOf could generate false negatives while getPrototypeOf won't.
function A() {};
function B() {};
(B.prototype = new A).constructor = B;
var b = new B;
// true
alert(Object.getPrototypeOf(b) === B.prototype);
// false
alert(Object.getPrototypeOf(b) === A.prototype);
// both TRUE!
alert(B.prototype.isPrototypeOf(b));
alert(A.prototype.isPrototypeOf(b));
Accordingly, the best function to emulate an instanceOf function over objects or "classes" could be:
function inherits(o, __proto__) {
// (C) Mit Style WebReflection suggestion
return ((
typeof __proto__ === "function" ?
__proto__.prototype :
__proto__
) || {}).isPrototypeOf(o);
};
// test case
function A() {};
function B() {};
(B.prototype = new A).constructor = B;
var b = new B;
// true,true,true,true
alert([
inherits(b, A),
inherits(b, B),
inherits(b, A.prototype),
inherits(b, B.prototype)
]);
More ES5 Friendly Patterns
If we use what ES5 is bringing into JavaScript and via native execution speed, we may be interested into more "articulated" patterns to define "classes", or simply classes instances.Object.defineProperties
First of all, as I have twitted already, please ignore that wrong suggestion about custom implementation.The only place where all those checks could make sense is inside Object.defineProperty, and not twice in both defineProperties AND definePrperty, since if latter exists, why on earth we should try to emulate its internal checks?
If defineProperty does NOT exists, why shoud we try to emulate it without checks? I hope MDC guys will remove that nonsense from that page, I'd love to be sure developers get ES5 properly, good practices included.
Back in the topic, here there is an example:
function A(_value) {
// implicit init method
// where we define privileged proeprties/methods
Object.defineProperties(this, {
_value:{
value:_value,
// as example since it is false by default
enumerable:false
}
});
// and eventually we perform some task
};
// Class definition
Object.defineProperties(A.prototype, {
toString:{
value:function () {
return "" + this._value;
},
configurable:false
}
});
var a = new A(123);
alert(a); // 123
Does it make sense? We have more power via defineProperties and we can reuse, or share, objects across the whole library without any kind of problem.
One thing we should be ware about, is that A.prototype is always re-assignable, so if we try to define the A prototype property itself as non configurable we won't be safer anyway, it can be overwritten!
Dual Behavior: Factory Emulator
// when use strict will be enabled
// to obtain dual behavior (factory/constructor)
function A(_value) {
"use strict";
return Object.defineProperties(this || new A(_value), {
//... privileged definition
});
};
Dual Behavior Via Object.create
// Object.create way
function A(_value) {
// note that "this" may be useless, A() same of new A()
// inheritance chained in place
return Object.create(A.prototype, {
_value:{
value:_value,
enumerable:false
}
});
};
A.prototype = Object.create(A.prototype, {
toString:{
value:function () {
return "" + this._value;
},
configurable:false
}
});
Confused ???
I know I have started saying that theoretically we don't need anymore a single function to implement inheritance via ES5, but I bet those Java guys won't ever accept the fact JavaScript has no Classes and this is why I have suggested different patterns so that everybody could be happy about these ES5 features.When Can We Start To Use These Features
Right now, in my opinion, including just one single vice versa project Object.js file, being careful about the target browser.Unfortunately, and as usual, Internet Explorer is behind every other browser and some method cannot be perfectly emulated.
This is also why I have decided to show both defineProperties and create way, since IE defineProperties works only with DOM prototypes and global Window (or generally speaking only with natives and not with object) so that create could be used without problems, so far avoiding get/set and considering that configuration properties may not be respected.
I know this is crap, but until IE9 or the day we'll finally decide to drop this browser support, there's not that much we can do: annoying!
P.S. to test all these natively, we can use a WebKit nightly build.
Wednesday, December 02, 2009
with: The World's Most Misunderstood Statement
Update If after this post and its comments you are not convinced yet, have a look into with: Some Good Example one.
Every reference to this post is purely casual ... no it's NOT!
It's time to talk about the fuss around the innocent with statement, and why somebody decided it's bad or it should not be part of next JavaScript.
but for some sadistic reason we decided that we don't like native extended chains in our code ...
Honestly, I could post thousands of bad examples and point my finger into this or that library, function, method, evaluation, etc etc ... it's like the myth eval is evil ... so how come one of the first one to talk about evil is using eval to speed up code interpretation?
Easy: there are cases and cases!
I wanna use the smallest amount of characters to obtain a script injection ... right?
Elegant, memory aware (no variables declarations, no need to create a closure), compact, efficient, cross-browser ... where are "JavaScripters" that used with to assign variables here?
with has been used since first ECMAScript 3rd Edition implementation and nobody has never had a single problem. Moreover, if we contest that we could cause a wrong variable assignment, what about a required closure plus a required var assigned to a possible node causing possible leaks plus name conflicts?
We remove a statement but we don't remove those developers that constantly forget to define local variables so they can destroy the global namespace?
I would go for the second specie before the first one!
In any case, I'd love to see who is able to produce a code smaller than the precedent example taking care about removing any trace of that operation in the current scope.
Wait a second ... in the compress everything to improve performances Web era we need to create a closure and a scoped variable over greater number of characters? ... REALLY? And what about the "non being sure point", does anybody check if document.documentElement, always present, has an insertBefore method? NO because sometimes we simply don't have to!
It's like blaming features because some silly junior developer could cause disasters:
... we cannot be serious here, can we? So, remove textarea HTML element because it could be used from somebody to write malicious code directly evaluated when the content changes ... is this the ES5 philosophy? I do hope no, but apparently, and at least for the with statement, it is.
Maybe we should consider the fact that if we have not control over our objects and scopes we can remove every evil thingy but the bad code will be still there ... isn't it? ... but it's a language fault, right?
The reason is that these parsers do not consider a with statement as an in-line scope and cannot predict in that moment the accessible outer scope, unless these parsers won't be able to virtually replicate every possible scenario in that scope.
Furthermore, since some compressor could optimize properties access, bytewise speaking, a with statement will make that optimization redundant:
If we use the string shortcut in the statement we'll assign the variable or raise an error (if the string is for some reason hard coded).
Another example is property access optimizations typical via ordered namespaces as YUI! dojo Closure or other libraries have.
Can you spot the difference? And what if in the middle of the closure there is a with?
These are just few examples where a statement like with could become uncomfortable ... but why nobody else has ever had this problem?
That "i" is referencing an instance variable and it does not requires the "this" prefix. So how come implicit scope is permitted in many other languages, Java and C# included, but we don't want an in-line implicit scope in JavaScript, as with statement basically is?
To avoid the error we need to define the value as global one:
In JavaScript we have the opposite scenario, what has been explicitly declared as local, arguments included, is local, everything else is considered from outer scopes or, if nothing there, global. It could sounds messy but this is all about JavaScript and closures so if we don't like it, why are we using JavaScript? Just use GWT or whatever hybrid way to end up with translated closures ... no? I see ... we think and we feel we are truly programming in Java, as example, rather than JavaScript, don't we? ... so, how come this unicorn became the most used programing language?
Anyway, we are not here to decide who made the best choice, we are here to discuss why with statement is still present in other programing languages, right?
The answer is simple: to make things simpler!
This Fredrik Lundh post is the first one I found searching an example ... so, does with make sense?
But what is wrong if we are sure that we are dealing with a variable and its method?
In few words, there are really thousands of bad examples online and rarely good one but the blamed one is always the with statement rather than people unable to understand it.
Another example, still twitter, few minutes ago ...
At the end of the day these developers scared by with do not simply use it, while developers able to use it in a proper way should say good-bye because somebody else decided that with statement should not be part of ES5: WTF ... I mean, am I the only one able to spot the beauty of the with statement here?
We could find solutions for arguments.callee but with statement ... guys, please be serious about that, and ask every other programing language to remove it, while somebody could find even the good old goto useful.
Remove features, for what? Performances? Who cares if it is rarely used, I do care if it is not possible to use anymore.
Every reference to this post is purely casual ... no it's NOT!
It's time to talk about the fuss around the innocent with statement, and why somebody decided it's bad or it should not be part of next JavaScript.
Extends the scope chain for a statement
Extended chains are probably what we love more about JavaScript libraries ...
$("stuff")
.more()
.again()
.somethingElse()
;
but for some sadistic reason we decided that we don't like native extended chains in our code ...
with(stuff){
more()
again()
somethingElse()
};
Bad Examples And Bad Usages
Every time I say that with statement has nothing bad, somebody perpetually points out this bloody post ... OK, from Yahoo! ... so what?Honestly, I could post thousands of bad examples and point my finger into this or that library, function, method, evaluation, etc etc ... it's like the myth eval is evil ... so how come one of the first one to talk about evil is using eval to speed up code interpretation?
Easy: there are cases and cases!
The Power Of with(){}
I give you the most basic example ever, something happened in twitter few minutes ago, OK?I wanna use the smallest amount of characters to obtain a script injection ... right?
with(document.documentElement)
insertBefore(
document.createElement("script"),
firstChild
)
.text = "alert(1)"
;
Elegant, memory aware (no variables declarations, no need to create a closure), compact, efficient, cross-browser ... where are "JavaScripters" that used with to assign variables here?
with has been used since first ECMAScript 3rd Edition implementation and nobody has never had a single problem. Moreover, if we contest that we could cause a wrong variable assignment, what about a required closure plus a required var assigned to a possible node causing possible leaks plus name conflicts?
We remove a statement but we don't remove those developers that constantly forget to define local variables so they can destroy the global namespace?
for(i = 0; ... ) // FIRED FFS!!!
I would go for the second specie before the first one!
In any case, I'd love to see who is able to produce a code smaller than the precedent example taking care about removing any trace of that operation in the current scope.
Pointless Points
Somebody could say: "doode, you are not sure that insertBefore is the document.documentElement " ... The classic piece of antagonist code would be 99.9% of cases this one:
// avoid global scope pollution
(function(e){
e.insertBefore(
document.createElement("script"),
e.firstChild
).text = "alert(1)"
;
// feeling cool and clever
// just to avoid "var"
})(document.documentElement);
Wait a second ... in the compress everything to improve performances Web era we need to create a closure and a scoped variable over greater number of characters? ... REALLY? And what about the "non being sure point", does anybody check if document.documentElement, always present, has an insertBefore method? NO because sometimes we simply don't have to!
Assignments
If we willing to remove a historical valid statement as with is because people could assign stuff there and they don't know where the stuff is assigned, we should remove every function able to evaluate code from a scripting language as JavaScript is.It's like blaming features because some silly junior developer could cause disasters:
<textarea
onchange='eval(this.value)'
></textarea>
... we cannot be serious here, can we? So, remove textarea HTML element because it could be used from somebody to write malicious code directly evaluated when the content changes ... is this the ES5 philosophy? I do hope no, but apparently, and at least for the with statement, it is.
Maybe we should consider the fact that if we have not control over our objects and scopes we can remove every evil thingy but the bad code will be still there ... isn't it? ... but it's a language fault, right?
Real Reasons To Remove with()
Compilers, munger, minifier, however we call them, these analyzers have hard life because of with statement.The reason is that these parsers do not consider a with statement as an in-line scope and cannot predict in that moment the accessible outer scope, unless these parsers won't be able to virtually replicate every possible scenario in that scope.
Furthermore, since some compressor could optimize properties access, bytewise speaking, a with statement will make that optimization redundant:
myO.doStuff(1);
myO.doStuff(2);
myO.doStuff(3);
// sometimes enough to decide
// doStuff could be optimized via
myO[s](4);
// and here it cannot
// or it requires extra effort ...
with(myO)
doStuff(5)
;
If we use the string shortcut in the statement we'll assign the variable or raise an error (if the string is for some reason hard coded).
Another example is property access optimizations typical via ordered namespaces as YUI! dojo Closure or other libraries have.
MyLib.namespace.stuff.doSomething(1);
MyLib.namespace.stuff.doSomething(2);
MyLib.namespace.stuff.doSomething(3);
// enough in this scope to optimize access via
var s = MyLib.namespace.stuff,
d = "doSomething"
;
s[d](4);
s[d](5);
s[d](6);
Can you spot the difference? And what if in the middle of the closure there is a with?
These are just few examples where a statement like with could become uncomfortable ... but why nobody else has ever had this problem?
Implicit this
If we would like to compare a closure with a classic Object Oriented Programming world, we could consider that most of the languages uses implicit reference inside methods.
function plusPlus(void):MyClass {
++i;
return this;
}
That "i" is referencing an instance variable and it does not requires the "this" prefix. So how come implicit scope is permitted in many other languages, Java and C# included, but we don't want an in-line implicit scope in JavaScript, as with statement basically is?
Python way
There is a basic rule in the Python local/global variable choice: explicit is better than implicit ... and I cannot agree more:
class MyOne():
value = 0
def add(self, i):
# requires to be explicit
self.value = self.value + i
# otherwise it will generates an error
# since value, as local, has not been defined
# value = value + i
value = 5
o = MyOne()
o.add(1)
o.value
To avoid the error we need to define the value as global one:
global value
value = value + 1
In JavaScript we have the opposite scenario, what has been explicitly declared as local, arguments included, is local, everything else is considered from outer scopes or, if nothing there, global. It could sounds messy but this is all about JavaScript and closures so if we don't like it, why are we using JavaScript? Just use GWT or whatever hybrid way to end up with translated closures ... no? I see ... we think and we feel we are truly programming in Java, as example, rather than JavaScript, don't we? ... so, how come this unicorn became the most used programing language?
Anyway, we are not here to decide who made the best choice, we are here to discuss why with statement is still present in other programing languages, right?
The answer is simple: to make things simpler!
This Fredrik Lundh post is the first one I found searching an example ... so, does with make sense?
If We Need It, Create A Reference
Imagine we have a global open function able to return a File instance ... now imagine we justify python because there is an implicit "as" to reference that pointer ...
// Python with style simulation
with({f:open("x.txt")}){
var data = f.read(1);
}
// if we need a temporary variable
// but we would like to be sure about
// variables in the scope
with({f:open("x.txt"), data:null}){
data = 123;
// do other stuff
}
But what is wrong if we are sure that we are dealing with a variable and its method?
// I don't want to care
// about variables declaration
// neither about leaks
// scope, names, etc ...
with(open("x.txt")){
if(read(1) === "+")
write("-")
;
close();
}
In few words, there are really thousands of bad examples online and rarely good one but the blamed one is always the with statement rather than people unable to understand it.
Another example, still twitter, few minutes ago ...
var obj = {};
obj.var1 = "objvar1";
var var1 = "globalvar1";
var var2 = "globalvar2";
// so we chose the object to use ...
with(obj) {
// .. and we have no idea what kind of object is it?
print(var1); // the object one, EASY!
// if we think obj should have a var2
// property but it does not
// we are doing wrong in any case
// and we don't know our instances
print(var2); // scoped var, EASY
// if we know what we are dealing with
// we have already resolved this problem
// knowing var1 and var2 values
var1 = "bar1";
var2 = "bar2";
// this is quite obvious ... isn't it?
// we have assigned these references one line ago
print(var1); //'bar1'
print(var2); //'bar2'
};
// is there anything unexpected here?
print(var1); // 'globalvar1'
print(var2); //'bar2'
print(obj.var1);//'bar1'
print(obj.var2);//undefined
As Summary
At the end of the day these developers scared by with do not simply use it, while developers able to use it in a proper way should say good-bye because somebody else decided that with statement should not be part of ES5: WTF ... I mean, am I the only one able to spot the beauty of the with statement here?
We could find solutions for arguments.callee but with statement ... guys, please be serious about that, and ask every other programing language to remove it, while somebody could find even the good old goto useful.
Remove features, for what? Performances? Who cares if it is rarely used, I do care if it is not possible to use anymore.
Even More Evil
with(document)
with(documentElement)
insertBefore(
createElement("script"),
firstChild
)
.text = "alert(1)"
;
Sunday, October 11, 2009
ES5 and function default scope
This is just a quick one. One of the worst decision about ES5, after the one to remove arguments.callee when "use strict" is present, is the missed default reference to the global object. Probably the most secure trick to retrieve the native window in JavaScript is this one:
I have already talked about this, and I am still against some ES5 "feature" which aim is probably the one to make JavaScript as fast as possible, regardless all code that will break if "use strict" is present in the generic scope.
Another non-sense is to allow a this in a context where there is no this. Rather than an error, ES5 will point refer this to undefined.
Are you kidding me? this is a special keywords related to the context and you are telling me a context is undefined? so I guess open(), as example, should throw an error there, since window cannot be implicit in an undefined context, isn't it? ... anyway, fortunately the whole problem could be quickly solved in this way:
This simple modification could avoid lots of headaches plus could avoid scope resolution for every window variable.
It's probably quite obvious but I can already imagine lots of people changing the whole function body rather than add a call at the end :D
The trick to retrieve the global context won't work in any case for nested functions, unless we are not sure that this points to window, but being the trick goal the one to make window resolution quick and safe, there is not window,self,top, whatever check that could solve the problem (unless [].sort() will be still buggy ...)
As summary, the day JavaScript will be that fast thanks to removed dynamic and run-time features, it won't be JavaScript anymore but another language a la ActionScript 2 when ActionScript 1 was enough, and the problem was the player rather than the language, which indeed has been replaced by ActionScript 3 even if the problem is still the player and not the language, since it can be compilable into old versions.
// wherever we are
var global = function(){return this}();
// while window, could be simply a variable
// assigned somewhere else in this scope
I have already talked about this, and I am still against some ES5 "feature" which aim is probably the one to make JavaScript as fast as possible, regardless all code that will break if "use strict" is present in the generic scope.
Another non-sense is to allow a this in a context where there is no this. Rather than an error, ES5 will point refer this to undefined.
Are you kidding me? this is a special keywords related to the context and you are telling me a context is undefined? so I guess open(), as example, should throw an error there, since window cannot be implicit in an undefined context, isn't it? ... anyway, fortunately the whole problem could be quickly solved in this way:
// ES5 problem
(function(){
"use strict";
this === undefined; // true ... LOL!!!
})();
// WebReflection QAD solution
(function(){
"use strict";
this === window; // true ... yeah baby!
}).call(this);
This simple modification could avoid lots of headaches plus could avoid scope resolution for every window variable.
It's probably quite obvious but I can already imagine lots of people changing the whole function body rather than add a call at the end :D
The trick to retrieve the global context won't work in any case for nested functions, unless we are not sure that this points to window, but being the trick goal the one to make window resolution quick and safe, there is not window,self,top, whatever check that could solve the problem (unless [].sort() will be still buggy ...)
As summary, the day JavaScript will be that fast thanks to removed dynamic and run-time features, it won't be JavaScript anymore but another language a la ActionScript 2 when ActionScript 1 was enough, and the problem was the player rather than the language, which indeed has been replaced by ActionScript 3 even if the problem is still the player and not the language, since it can be compilable into old versions.
Subscribe to:
Comments (Atom)