My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.
Showing posts with label Mootools. Show all posts
Showing posts with label Mootools. Show all posts

Monday, January 25, 2010

The JavaScript _super Bullshit

I know you already hate the title, but that's how I called one paragraph of my precedent post: Better JavaScript Classes.

This post is mainly dedicated for both libraries authors, and those Classic OOP Developers that still think JavaScript should be used in a classic way.

_super or parent Are Problematic!

It's not just about performances, where "the magic" may need to replace, wrap, and assign runtime everything in order to make it happens, it's about inconsistencies, or infinite loops, or hard debug, or disaster prone approach as well, since as I have already said instances have nothing to do with _super/parent .... so, how are things? Thanks for asking!

Real OOP Behavior

It's the ABC, and nothing else, if we call a parent/super inside a method, this method will execute with a temporary self/this context.
This means that the instance will be still:
  1. an instanceof its Class
  2. only that method will be executed, it is possible to call other super/parent accordingly with the hierarchy
  3. if the instance Class overrides a parent method, when this will be executed via super, the invoked "extra" method will still be the one defined in the instance class and nothing else

// Classic OOP Behavior, a simple PHP 5 example
class A {
public function __construct() {
echo 'A.__construct', '<br />';

// this will call the B method indeed
$this->hello();
}
public function hello() {
echo 'A.hello()', '<br />';
}
}

class B extends A {
public function __construct() {
// parent invocation, this has nothing to do with the instance
parent::__construct();
echo 'B.__construct', '<br />';
}
public function hello() {
echo 'B.hello()', '<br />';
}
}

new A;
// A.__construct
// A.hello()

new B;
// A.__construct
// B.hello()
// B.__construct

Even if most frameworks respect above behavior, there is still somebody convinced that _super/parent is something truly simple to implement, often replacing this references runtime without taking care of multiple hierarchies, more than a super, method specific super, and on and on ...
In few words, if above behavior is respected, and the best test case is with more than 2 extended classes, we could think we are half way there ... isn't it?

John Resig on Simple JavaScript Inheritance

John called it simple, and it is simple indeed. Inheritance in JavaScript has a name: prototype chain.
John did a good job for those few bytes, the runtime replaced method is temporarily the called one, so other methods will be invoked as expected.
Everything perfect? Not really, some "tiny little problem" could occur if for some reason something goes wrong.

var A = Class.extend({
init: function () {
document.write("A.constructor<br />");
},
hello: function () {
throw new Error;
document.write("A.hello()<br />");
}
});

var B = A.extend({
init: function () {
this._super();
document.write("B.constructor<br />");
},
hello: function () {
this._super();
document.write("B.hello()<br />");
}
});

setTimeout(function () {
alert(b._super === A.prototype.hello);
// TRUE!
}, 1000);
var b = new B;
b.hello();

This is what I mean when I talk about DPP, something goes wrong? The instance will be messed up.
Don't even think about a try catch for each method invocation, this will make your application extremely slow compared with everything else.
In any case, 5 stars for its simplicity, but think before you decide to use _super in any case.

MooTools Way

Update My apologies to MooTools team. I had no time to re-test a false positive and MooTools is partially secured behind its parent call. However, my point of view about chose magic is well described at the end of this post comments, enjoy.

The Prototype Way

So we have seen already _super and parent problems, both wrongly attached into the instance, but we have not seen yet the Prototype way: the $super argument!

var A = Class.create({
initialize: function () {
document.write("A.constructor<br />");
},
hello: function () {
throw new Error;
document.write("A.hello()<br />");
}
});

var B = Class.create(A, {
initialize: function ($super) {
$super();
document.write("B.constructor<br />");
},
hello: function ($super) {
$super();
document.write("B.hello()<br />");
}
});

setTimeout(function () {
alert(b);
// nothing to do
}, 1000);
var b = new B;
b.hello();

Eventually, Prototype library got it right, YES!!!
While every other is associating super to the instance, Prototype understood that super has nothing to do with the instance.
Every method which aim is to override the inherited one must have a $super argument, if we want a limitation but finally the only implementation that does not break anything, whatever happens in the super call.
This is correct, the instance is temporarily injected into the super method and nothing else. No self referencing, no run-time assignments that could break, simply the method, that will host for a call that instance.
Every other method will be wrapped in order to bring the current instance in the super one and in this way we can consider our code somehow safer, the whole application won't break if something goes unpredictably wrong!

Still Something To Argue About

While I have never thought that Prototype got it absolutely right, I must disagree about its Class implementation.
It's that simple, I have already said it, in JavaScript Classes are functions!
Accordingly, why on earth Class should be an object?

function Class(){
return Class.create.apply(null, arguments);
};
Object.extend(Class, {
// the current Class object
});
Class.prototype = Function.prototype;

With a ridiculous effort, Prototype library could be the first one to implement a proper Factory to create classes!

// valid, for OO "new" everything maniacs
var A = new Class(...);

// still valid, alias of Class.create
var B = Class(...);

// explicit Factory
var C = Class.create(...);

// there we are!
alert([
A instanceof Class, // true
B instanceof Class, // true
C instanceof Class, // true
A instanceof Function, // of course
B instanceof Function, // of course
C instanceof Function // of course
]);


How Things Work "There"

The second thing I must disagree about Prototype is the way arguments are retrieved.
The king of public prototypes pollution framework does not cache the Function.prototype.toString to avoid overrides and/or redefinition:

function Fake($super){};
alert(Fake.argumentNames());

Function.prototype.toString = function () {
return this.name || "anonymous";
};

// throws an error
// alert(Fake.argumentNames());

// Fake is a first class *Object*
Fake.toString = function () {
return "[class Fake]";
};
// throws an error
//alert(Fake.argumentNames());

Fake.toString = function () {
return "function Fake(){return Fake instances}";
};
// empty string
alert(Fake.argumentNames());

Are you kidding me? Let's try again:

Function.prototype.argumentNames = (function (toString) {
return function() {
var names = toString.call(this).match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
}
})(Function.prototype.toString);

4 out of 4 successful arguments parsing, rather than errors, problems, etc etc, for a framework that is already obtrusive, but at least in this case did not consider obtrusive code at all ... please fix it!

Inevitable Unportable

Finally, since the Prototype way requires wrappers and first argument injection, attached methods become instantly not portable anymore.
If we define a subclass and we would like to recycle its method somewhere else, we are trapped by the replaced, wrapped, injected $super argument.
Unfortunately this is the worst side effect for something that is actually not even close to classic OOP ... but we need to accept this compromise, or we simply need to better understand JavaScript, isn't it?

As Summary

This post does not want to be a "blame everybody for free" one, this post simply shows why I have written my Class implementation and why I do prefer explicit calls (somebody called them hard-coded) to super, shared, parent, whatever, inherited prototypes, as is for any other function we need in the middle of a session. JavaScript is like that, every function could be called without any warning somewhere with a different "this" reference and this is actually one of the most interesting and beauty part of this language.

I hope I have been able to give hints to both libraries authors, just 3 in this case, and specially to developers, those that copya and paste code trusting the source but unable to perform these kind of tests, and those convinced that JavaScript OOP and this._super is cool and it makes sense. As you can see, it does not, logically speaking first, and technically speaking after.
Last, but not least, all these wrappers simply mean less performances, even if we are talking about small numbers (a whole framework/libary based over these small numbers can easily become a pachyderm, seconds slower than many others).

Thursday, February 19, 2009

On JavaScript Inheritance Performance and Libraries Troubles

Update: I have replied to myself and developers in a new post. I would like to say a big Thank You to every developer exposed tests, benchmark, traps, and considerations. Please read my last thoughts about the subject, since I deeply reconsidered my position.
Update: Please do not get me wrong. I have no intention to say that one or any of cited framework, piece of code, library, is not good or fast enough to extend other classes. This post is about maniac optimization based on personal considerations over some deeper analysis to complete in a way the first benchmark.
I am not criticizing libraries, they are great and they offer everything, I am simply showing a specific case, which is particular on purpose, and a specific behavior that, useful or not, could not be respected.


About


Few days ago Ajaxian published a post about JS inherited methods performances via common libraries strategies.
I think the argument is extremely interesting but there's not enough material yet, so here I am with my contribution plus a "best option" proposal.

Why We Need Libraries to Extend Functions


  • libraries (should) manage properly the prototypal chain

  • libraries (should) make code more elegant/readable

  • libraries (should) let us create overrides being able to call parent/super method implicitly if necessary

  • libraries are often based over a generic extended "class" or hierarchy and users should be familiar with that way to extend or create classes in order to avoid internal conflicts



Why We Do Not Need Libraries to Extend Functions


  • we are performances or bytes maniacs and we are scared by library obtrusive implementations

  • we don't trust the library pattern/strategy to extend function - we know a better way to do it simply and quickly

  • we would like to create hybrids unmanageable via used library


About above points, let me answer to this question:
"Which Common Code/Library is Good and Fast to Extend Properly?" Considering the YUI approach out of the game, NOT A SINGLE ONE!!!

Tested Libraries in Alphabetic Order

base2, dojo, jClass, MooTools, Prototype, WebReflection (blog proposal), and YUI


About the Test

I tried to create a particular inheritance case.
There is a parent class with a toString and a toLocaleString prototype method.
These methods are particular since they are hidden methods (read: native) and browsers like Internet Explorer do not discover hidden methods even if those have been explicitly assigned.
The extended class should be able to understand the inheritance without problems and produce the expected result, showed in the Ad Hoc example.


Ad Hoc concept and troubles


  • Concept: manual inheritance over usable constructors.

  • Troubles: requires a bit of skill and parent calls are always explicit.



// manual implementation
// best performances and expected behavior in every browser
function AdHocClass(name){
this.name = name;
};
AdHocClass.prototype.toString = function(){
return this.name;
};
AdHocClass.prototype.toLocaleString = function(){
return "locale: " + this.toString();
};
function AdHocSub(name, age){
AdHocClass.call(this, name);
this.age = age;
};
AdHocSub.prototype = new AdHocClass;
AdHocSub.prototype.constructor = AdHocSub;
AdHocSub.prototype.toString = function(){
return AdHocClass.prototype.toString.call(this) + ": " + this.age;
};

// This is the expected result We would like to obtain
var me = new AdHocSub("Andrea", 30);
alert(me); // Andrea: 30
alert(me.toLocaleString()); // locale: Andrea: 30



base2


  • Concept: runtime injected parent (called via base) over explicit initialization via init method

  • Troubles: every instance calls two functions when initialized: the contructor plus the init method. This slows down performances. At the same time, not every method is inherited properly.



var BaseClass = base2.Base.extend({
constructor:function(name){
this.name = name
},
toString:function(){
return this.name;
},
toLocaleString:function(){return "locale: " + this.toString()}
});
var BaseSub = BaseClass.extend({
constructor:function(name, age){
this.base(name);
this.age = age;
},
toString:function(){
return this.base() + ": " + this.age;
}
});
var me = new BaseSub("Andrea", 30);
alert(me); // Andrea: 30
alert(me.toLocaleString()); // ERROR: same as toString both IE and Others



dojo


  • Concept: lazy dependencies resolution via the initializer method

  • Troubles: the initializer method is not that linear and it is probably the slowest in the list. Sometimes we need to call explicitly the method, as first argument, while sometimes we need to modify the arguments object or create an array to call the parent method. I am not sure I used dojo best practices to replicate the scenario but that's what I found googling for a while (Update: thanks to Eugene Lazutkin I could use a better practice which improved consistently performances). Is the rest fine? Not really, Internet Explorer does not inherit some hidden method. Update: apparently dojo offers different possibilities to extend and "play" with parents, so this is just one of them.


dojo.declare("dojoClass", null, {
constructor:function(name){
this.name = name;
},
toString:function(){
return this.name;
},
toLocaleString:function(){return "locale: " + this.toString()}
});
dojo.declare("dojoSub", dojoClass, {
constructor:function(name, age){
this.age = age;
},
toString:function($super){
return this.inherited("toString", arguments) + ": " + this.age;
}
});
var me = new dojoSub("Andrea", 30);
alert(me); // Andrea: 30 - not in IE
alert(me.toLocaleString()); // ERROR: same as toString in IE



jClass


  • Concept: basically the same one used by base2

  • Troubles: even worse than base2 since there are no explicit searches for toString and valueOf (most common hidden methods)



var ResigClass = jClass.extend({
init:function(name){
this.name = name;
},
toString:function(){
return this.name;
},
toLocaleString:function(){return "locale: " + this.toString()}
});
var ResigSub = ResigClass.extend({
init:function(name, age){
this._super(name);
this.age = age;
},
toString:function(){
return this._super() + ": " + this.age;
}
});
var me = new ResigSub("Andrea", 30);
alert(me); // Error: [object Object] in IE
alert(me.toLocaleString()); // Error: [object Object] in IE



MooTools


  • Concept: runtime injected parent, similar to base2. In my opinion the most clean and elegant way to extend a class.

  • Troubles: Classes are created via Class constructor so we have same bottleneck spotted in base2. Inheritance problems are basically the same found in base2: some hidden method is not inherited.



var MooToolsClass = new Class({
initialize: function(name){
this.name = name;
},
toString:function(){
return this.name;
},
toLocaleString:function(){return "locale: " + this.toString()}
});
var MooToolsSub = new Class({
Extends:MooToolsClass,
initialize:function(name, age){
this.parent(name);
this.age = age;
},
toString:function($super){
return this.parent() + ": " + this.age;
}
});
var me = new MooToolsSub("Andrea", 30);
alert(me); // Andrea: 30 - not inherited in IE
alert(me.toLocaleString()); // ERROR: locale: Andrea in IE



Prototype


  • Concept: $super variable as parent sent as first argument, imho the most obtrusive way so far and not elegant/intuitive at all.

  • Troubles: something works as expected, but only toString and valueOf, as is for base2, are considered hidden methods. The extra argument and the method overload make this library almost slow as dojo inheritance implementation is.



var ProtoClass = Class.create({
initialize:function(name){
this.name = name;
},
toString:function(){
return this.name;
},
toLocaleString:function(){return "locale: " + this.toString()}
});
var ProtoSub = Class.create(ProtoClass, {
initialize:function($super, name, age){
$super(name);
this.age = age;
},
toString:function($super){
return $super() + ": " + this.age;
}
});
var me = new ProtoSub("Andrea", 30);
alert(me); // Andrea: 30
alert(me.toLocaleString()); // ERROR: same as toString in IE




YUI


  • Concept: a constructor link to its parent prototype via "this.constructor.superclass". No runtime operations, no injected overloads, just "raw methods" based on explicit calls.

  • Troubles: not really. The YUI approach is extremely simple and efficient then "of course it works"!. There are just a couple of steps resolved via YAHOO.lang.extend: the correct constructor reassignment after the prototype one, plus the superclass link. I have just a comment about YUI implementation: guys, why do not you avoid that function creation for each extend call?



// No var F = function(){}, i SUGGESTION
extend: function(F){
return function(subc, superc, overrides) {
if (!superc||!subc) {
throw new Error("extend failed, please check that " +
"all dependencies are included.");
}
F.prototype=superc.prototype;
subc.prototype=new F;
subc.prototype.constructor=subc;
subc.superclass=superc.prototype;
if (superc.prototype.constructor == OP.constructor) {
superc.prototype.constructor=superc;
}

if (overrides) {
for (var i in overrides) {
if (L.hasOwnProperty(overrides, i)) {
subc.prototype[i]=overrides[i];
}
}

L._IEEnumFix(subc.prototype, overrides);
}

}
}(function(){}),

And here we are with the scenario:

function YUIClass(name){
this.name = name;
};
YUIClass.prototype.toString = function(){
return this.name;
};
YUIClass.prototype.toLocaleString = function(){
return "locale: " + this.toString();
};
function YUISub(name, age){
this.constructor.superclass.constructor.call(this, name);
this.age = age;
};
YAHOO.lang.extend(YUISub, YUIClass);
YUISub.prototype.toString = function(){
return this.constructor.superclass.toString.call(this) + ": " + this.age;
};
var me = new YUISub("Andrea", 30);
alert(me); // Andrea: 30
alert(me.toLocaleString()); // locale: Andrea: 30



WebReflection Proposal

After an analysis like this one, how could I skip my "all the best from others without troubles" proposal?
These are my considerations:

  • The fastest way to use a parent method is an explicit call

  • The best way to perform above step is via a link able to remove explicit dependencies (read: MyParentClassName instead of this.constructor.superclass). In this way methods could be easily transported from a prototype to another one without problems (less memory usage and less code to maintain)

  • The this.parent() solution is the cleanest one and generally speaking more close to our concept of "Object Oriented JavaScript". It is portable, it is meaningful, it is shorter than an explicit call with or without a link but it is not that fast to execute.

  • Using best practices to cache all we need, create a quick wrapper, resolving hidden methods problems, will not bring us to native or YUI performances, but at least in a good scenario: fast enough to guarantee performances over code elegance and readability


So here we are with my proposal:

  • Concept: parent in prototype but changed runtime for hierarchy purpose only if necessary (read: only for overrides). This will allows us to call parent directly in the constructor, as example, so we won't have double calls for each instance. At the same time, this implementation lets us call explicitly a parent method from those whose were not inherited.

  • Troubles: performances are the best but still far from native one.



function WRClass(name){
this.name = name;
};
WRClass.prototype.toString = function(){
return this.name;
};
WRClass.prototype.toLocaleString = function(){
return "locale: " + this.toString();
};
function WRSub(name, age){
this.parent(name);
this.age = age;
};
wr.extend(WRSub, WRClass, {
toString:function(){
return this.parent() + ": " + this.age;
}
});
var me = new WRSub("Andrea", 30);
alert(me); // Andrea: 30
alert(me.toLocaleString()); // locale: Andrea: 30

// last, but not least
WRSub.prototype.explicitParentToString = function(){
return this.parent.prototype.toString.call(this);
// instead of
return this.constructor.superclass.toString.call(this);
};
alert(me.explicitParentToString()); // Andrea



WebReflection proposed Extend

For future improvements/bugs fixes, please use this link.

var wr = {
extend:function(parent, extend){
/**
* (C) Andrea Giamamrchi
* Mit Style License
*/
return function(self, Function, Object){
var prototype = function(){
this.prototype[key] =
this.prototype.parent && typeof Object[key] == "function" && typeof this.prototype[key] == "function" ?
extend(Function, this.prototype[key], Object[key]) :
Object[key]
;
};
if(Object){
parent.prototype = Function.prototype;
self.prototype = new parent;
self.prototype.constructor = self;
self.prototype.parent = Function.prototype.parent ?
extend(Function, Function.prototype.parent, Function) : Function
} else
Object = Function;
for(var key in Object)
prototype.call(self);
for(key in {toString:key})
return self;
//* ... for Internet Explorer only ...
for(var
split = "hasOwnProperty.isPrototypeOf.propertyIsEnumerable.toLocaleString.toString.valueOf".split(".");
key = split.shift();
)
if(Object.hasOwnProperty(key))
prototype.call(self);
//*/
return self
}
}(
function(){},
function(parent, extend, Function){
return function(){
this.parent = extend;
var result = Function.apply(this, arguments);
this.parent = parent;
return result
}
}
)
};



The Benchmark

I had to choose Prototype or MooTools, since these frameworks seem to have some problem to coexist. Since Prototype has been tested in the Ajaxian post, I decided to put MooTools in the middle.
Something to consider before you try the benchmark page ... I removed the purposeless direct method call since it is absolutely the same for every basic class, created via library or not.
The order is by performances, where generally speaking are these:

  1. Ad Hoc Manual Explicit Inheritance

  2. YUI lang.extend (close to Ad Hoc)

  3. wr.extend - WebReflection proposal (closer to jClass than YUI)

  4. jClass (truly close to base2)

  5. base2 (truly close to jClass)

  6. dojo

  7. Prototype

  8. MooTools


Try out The Benchmark

And have a nice week end ;-)

Tuesday, August 05, 2008

Image Protector, a nice waste of time

I've read right now about another, pointless, attempt, to avoid the classic Save Image As from a website.

Guys, we are in 2008, and I hope that everybody knows what can the simple Stamp / Print button can do, when we are simply watching whatever in our screen.

No way, every tot months, somebody "creates" the ultimate version of an Image Protector, usually based on JavaScript, then "startly pointless" because JS could be easily disabled.

Morevore, this times the trickless trick even requires an excellent library as MooTools is.

Well, since I hate disinformations about techniques to make data truly safe, I can tell you that you need 159 characters to remove the protecion, javascript protocol included.

The funny stuff is that basing the same anti protector over MooTools, since this library is required for the amazing protector, you need even less characters to do the same, basing them on a $$("img") call, instead of document.getElementsByTagName.

Being sure that this technique will be probably adopted from people that do not know a single thing about web or security, and do not know how to implement a basic Watermark, I suppose somebody will implement the same trickless trick, so I prefer to show you the extended link that, if saved in your bookemark, will be able to remove the protection whenever you need, and in a click.

This is the code:

javascript:(function(b,r,l){l=b.length;while(0<l--)r.test(b[l].src)&&b[l].parentNode.removeChild(b[l--]);})(document.getElementsByTagName("img"),/blank\.gif$/);


And this is the link:
noMooreProtection

Save into bookmark, drag there, try the example page, one click in the "bookmarked magic guru crack", and sweat dreams, you can still grab images from those sites that do not have a clue about safe contents :D