My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Monday, July 30, 2007

JavaScript ElementTraversal proposal

27 July W3 announced a new Working Draft, called: ElementTraversal

This is a "new" feature but today it's not cross browser so this is my proposal to have Element Trasversal with every recent browser:

(ElementTraversal = function(node){
this.node = node;
this.firstElementChild = this.getFirstElementChild();
this.lastElementChild = this.getLastElementChild();
this.previousElementSibling = this.getPreviousElementSibling();
this.nextElementSibling = this.getNextElementSibling();
this.childElementCount = this.getChildElementCount();

}).extend(null, {
getFirstElementChild:function(){
for(var c = this.node.childNodes,
i = 0,
j = (c && c.length) || 0,
e;
i < j;
i++
){
if(c[i].nodeType === 1)
e = c[j = i];
};
return e;
},
getLastElementChild:function(){
for(var c = this.node.childNodes,
i = ((c && c.length) || 0) - 1,
j = -1,
e;
i > j;
i--
){
if(c[i].nodeType === 1)
e = c[j = i];
};
return e;
},
getPreviousElementSibling:function(){
var e = this.node.previousSibling;
while(e && e.nodeType !== 1)
e = e.previousSibling;
return e;
},
getNextElementSibling:function(){
var e = this.node.nextSibling;
while(e && e.nodeType !== 1)
e = e.nextSibling;
return e;
},
getChildElementCount:function(){
for(var c = this.node.childNodes,
i = 0,
j = (c && c.length) || 0,
n = 0;
i < j;
i++
){
if(c[i].nodeType === 1)
++n;
};
return n;
},
update:function(){
this.constructor.call(this, this.node);
return this;
}
});


And this is a basic test:


test


test

test
test

test




My proposal is based on extend Function prototype, You can find them here.

Just a note about update extra method: not every browser support a defineGetter / setter method so update is a quick way to have correct properties.

If You create an instance and You modify some node, You should use:

ET.update();

This method returns instance itself, so You can pass them directly on other functions.

Saturday, July 28, 2007

JavaScript Blackboard

Does We really need only canvas element to draw something?

This is a basic blackboard example that uses my extend.js proposal and my setInterval and setTimeout with arguments fix for Internet Explorer.

Just draw something on Blackboard and have fun with JavaScript ;-)

Thursday, July 26, 2007

625 bytes to extend JavaScript

These days I've studied (again) JavaScript prototypal inheritance and common extend functions or methods used by different libraries.
This post is a summary about my experiments and every example code is based on this script.

Probably someone is thinking about my unreadable source code, however it's self packed and quite clear, atleast for me.
The goal was to write 2 basic prototypes to extend objects or functions using "best practices" to do them using less chars as possible ... so, goal done ;-)

If You're interested about these prototypes logic please tell me, I'll try to find more time to explain better each one.

Object.prototype.extend
This is the first proposal and it's really simple to use/understand.

var a = {a:"a"},
b = {b:"b"}.extend(a);
alert([b.a, b.b]); // a,b

This is a basic example.
Extend prototype works with object itself and return them after a for in loop discarding prototyped methods.

function A(value){
this.value = value;
};
A.prototype.getValue = function(){
return this.value;
};

var a = new A("a"),
b = {}.extend(a);
alert([b.value, b.getValue]); // a,undefined

Since extend works with object itself You can easyly add properties or methods in this way:

var a = {a:"a"};
a.extend({
getValue:function(){
return this.value;
},
setValue:function(value){
this.value = value
return this.getValue();
}
});

alert([a.a, a.setValue("b")]); // a,b

This prototype accepts one or more argument, so You can produce last example in this way too:

var a = {a:"a"},
b = {setValue:function(value){
this.value = value
return this.getValue();
}};
a.extend({getValue:function(){return this.value}},b);

alert([a.a, a.setValue("b")]); // a,b

Obviously, extend prototype sets correctly toString and valueOf methods with IE too, what You just need to remember is that if extended objects have two methods with the same name, last one only will be available:

var a = {a:"a"}.extend(
{
toString:function(){return "a"},
valueOf:function(){return this.a}
},
{
toString:function(){return "Object a"}
}
);

alert([a,a.valueOf()]); // Object a,a

That's all, Object.prototype.extend is just what You need when You need to extend some object.
Only a last note about extend, it should work with other variables type too but please remember that for in loop doesn't always respect assignment order:

var a = [,,,4,5,6].extend([1,2,3]);
alert(a); // 1,2,3,4,5,6

This is "a case" and not a rule so please use concat native method to extend Arrays or test for in loops before You use them with variables that are not instances or native objects.


Function.prototype.extend
This is my second prototype proposal, partially based on first one but really more powerful.
This prototype extends constructors and return them adding special features that other libraries don't use.

Its behaviour is described on this post about JavaScript prototypal inheritance but this prototype is better than simple $extend function showed on this post.

As first point, this prototype assigns correctly constructor used to create other one.
This seems to be a "natural" behaviour but I'm sure that not every other library assign them correctly.
The constructor property is not "secure" as instanceof check but first one could be easyly compared, for example, inside a swtich:

switch(genericInstance.constrcutor){
case Array://doStuff
break;
case Mine://doOtherStuff
break;
};

With more complex code this feature is not so unusual while instanceof requires a lot of if ... else if ... else.
This property is useful for a lot of other pieces of code too, so why We shouldn't have a correct constructor property when We create or extend another one?

This is another example that has a "not everytime" respected behaviour (using other libraries):

function A(){};
function B(){};
B.extend(A);

alert([

(new B).constructor === B, // true
(new B).parent === A, // true
(new B) instanceof A, // true
(new B) instanceof B, // true
(new A) instanceof B // false

].join("\n"));


The second setted property, showed in last example too, is parent one, that's a referer to parent constructor if exists, undefined value otherwise.

This property should be useful as constructor to do one or more operations using parent insteadof constrcutor.

Third point about Function.prototype.extend is Super, automatically inherited extra special method.


What's Super?
Super is a method that supports multiple (cascade) inheritance, starting from instance parent and correctly respected on its parent too.

The parent property isn't always enought to support multiple parents methods.
Look at this example:

function A(){};
A.prototype.init = function(name){
this.name = name;
return this;
};

(B = function(){}).extend(
A,
{init:function(name){
var result = this.parent.prototype.init.call(this, name);
this.name += " from B";
return result;
}}
);

C = function(){};
C.extend(
B,
{init:function(name){
this.parent.prototype.init.call(this, name);
this.name += " from C";
return this;
}
});

If You think that a new C instance will not crash or block your browser You're wrong!
Since apply or call methods inject temporary into another method (function) scope a different this referer, You should think about B.prototype.init one.
This method will use a C instance as this referer but C instance will have its own properties and its own methods.
Infact C instance will have a parent property too that will be exactely B constrcutor so above example, using a new C instance, will loop recursively calling as many times as it can B.prototype.init.

The solution is really simple, just use explicitally constrcutor as showed in this example:

function A(){};
A.prototype.init = function(name){
this.name = name;
return this;
};

(B = function(){}).extend(
A,
{init:function(name){
var result = A.prototype.init.call(this, name);
this.name += " from B";
return result;
}}
);

C = function(){};
C.extend(
B,
{init:function(name){
B.prototype.init.call(this, name);
this.name += " from C";
return this;
}
});

alert((new C).init("A").name); // A from B from C

Is it ok? Of course, it works perfectly.
However other languages have a dedicated keyword to use constructor recoursively or not and this keyword should be parent or super one.
In JavaScript the super keyword is reserved so it should be a good idea to don't use them if it's not absolutely necessary (used as string, for example), that's why I've created a Super magic method and this is how does it work:

function A(){};
A.prototype.init = function(name){
this.name = name;
return this;
};

(B = function(){}).extend(
A,
{init:function(name){
var result = this.Super("init", name);
this.name += " from B";
return result;
}}
);

C = function(){};
C.extend(
B,
{init:function(name){
this.Super("init", name);
this.name += " from C";
return this;
}
});

alert((new C).init("A").name); // A from B from C

Simple? I hope them and this way to use inheritance is quite cool (imho) ;-)
You don't need to care about constrcutor name and You don't need to use call or apply, just specify parent method name to call or, if You need to call super constructor, just use a nullable value such 0, "", undefined, false or null.

This is, for example, the Kevin Lindsey demostration code, adapted with my extend prototype:

function Person(first, last) {
this.first = first;
this.last = last;
};
Person.prototype.toString = function() {
return this.first + " " + this.last;
};

function Employee(first, last, id){
this.Super(null, first, last);
this.id = id;
};
Employee.extend(
Person,{
toString:function() {
return this.Super("toString") + ": " + this.id;
}
});

function Manager(first, last, id, department) {
this.Super(null, first, last, id);
this.department = department;
};
Manager.extend(
Employee,{
toString:function() {
return this.Super("toString") + ": " + this.department;
}
});

alert([

new Person("John", "Dough"),
new Employee("Bill", "Joi", 10),
new Manager("Bob", "Bark", 20, "Accounting")

].join("\n"));

... that in a more scriptish way should become this piece of code:

(Manager = function(first, last, id, department){
this.Super(null, first, last, id);
this.department = department;
}).extend(
(Employee = function(first, last, id){
this.Super(null, first, last);
this.id = id;
}).extend(
(Person = function(first, last){
this.first = first;
this.last = last;
}).extend(
null,
{toString:function(){return this.first + " " + this.last}}
),
{toString:function(){return this.Super("toString") + ": " + this.id}}
),
{toString:function(){return this.Super("toString") + ": " + this.department}}
);

Last point is that if You don't specify a constructor (function) as first Function.prototype.extend argument You'll recieve just original one:

var valueOf = {valueOf:function(){return this.constructor}};
A = (function(name){
this.name = name
}).extend(
null,
{toString:function(){return this.name}},
valueOf
);

alert([(new A("test")), (new A).valueOf() === A]); // test, true

So do You like these 625 prototypal bytes?

Quick Fix - Internet Explorer and toString problems

As You can read on precedent post I'm testing common libraries extend behaviour, both for objects or constructors.

One feature that many developers seems to ignore is IE behaviour when an object contains an explicit toString method.

Look at this simple example:

for(var key in {toString:function(){return "bye bye IE"}})
alert(key);

now test them with IE 5, 5.5, 6 or last 7 version.

What's up? ... absolutely nothing ... so what the hell?

IE doesn't recognize that toString method as explicit one but it "thinks" that's a native one.

This means that a lot of extend functions doesn't assign correctly this method if client broser is IE so every extended object will simply have native or prototyped toString method and not new explicit one.

How to fix this IE behaviour ?

// generic object/instance
var o = {
a:"a",
b:"b",
toString:function(){
return "hello IE too"
}
};

for(var key in o){
// doSttuff ... for example
alert(key);
};
/*@cc_on if(o.toString!=={}.toString){
// doStuff ... for example
alert(o.toString);
}@*/

Since only IE has this problem a conditional comment should be a good solution but this is not required, every other browser will simply check/assign one more time toString method if present.

You can use Object.prototype.toString instead of {}.toString but I think performances are not a problem in both cases.

What are libraries that don't care about this IE problem?

It seems that jQuery, MooTools and probably other libraries don't care about this IE problem while dojo, for example, does more than one check:

if(dojo.isIE &&
(typeof(props["toString"]) == "function") &&
(props["toString"] != obj["toString"]) &&
(props["toString"] != tobj["toString"])
)
obj.toString = props.toString;


Dean Edwards checked valueOf prototype too, so the complete fix should be this one:

/*@cc_on
if(o.toString!==Object.prototype.toString){
// doStuff
}
if(o.valueOf!==Object.prototype.valueOf){
// doStuff
}
@*/

Wednesday, July 25, 2007

JavaScript prototypal inheritance using known libraries

JavaScript is a "quite old" scripting language but every day there's some news about its normal behaviour or some common code ... well, today is my round :-)

There are a lot of frameworks or libraries We use every day but I think every JS developer should know atleast basic JavaScript prototypal inheritance.
It seems to be the ABC of this wonderful scripting language but if You'll read this post I suppose You'll be surprised!

Let me start with a simple example about JavaScript inheritance, a constructor A with just one prototyped method and a sort of subclass B that would be extend A one.
So, this is the A constructor:

function A(){};
A.prototype.constructorIs = function(F){return this.constructor === F};

// alert((new A).constructorIs(A)); // true

This is a basic constructor example and every instance will inherit constructorIs method.
This one will return instance.constructor and compare them with a generic constructor Function.
Well, at this point We need just another constructor that would extend A one ... and its name is, for a real fantasy example, B.

function B(){};
B.prototype = new A;

Well done, this is a simple JavaScript inheritance example.
Every B instance will be an instanceof B and an instanceof A but there's a strange JavaScript behaviour with this kind of inheritance:
every instanceof B will have a constructor property that will be exactely inherited one!

alert((new B).constructor === A); // true!!!

Since prototype inheritance works in this way, last example code shows a correct behaviour.
This is true just because if new Function inherits from Function.prototype and every Object, as prototype parameter "is", is assigned by reference and not by a copy.

However JavaScript has not an official or crossbrowser way to get last inherited instance constructor so this behaviour should be a problem when You write a bit complex code or use multiple inherited constructors (someone call them classes).

At this point if You choose to implement a generic constructor extend method or function, I suppose You should clean this behaviour adding a better one ... or if You don't think so, please don't call that function/method extend and choose another name such injecting arbitraty methods or properties!

What I mean is that native JavaScript behaviour, using A and B constructors, is this one:

var a = new A,
b = new B;

alert([

a.constructorIs(A), // true
b.constructorIs(B), // false
a instanceof A, // true
b instanceof A, // true
b instanceof B, // true
a instanceof B, // false
a instanceof Function, // false
b instanceof Function // false

].join("\n"));

As I've said, this behaviour is logically correct but I suppose that every Object Oriented developer would have atleast one more true in that boolean list:
an instanceof B should have a B constructor!

This is why a lot of people wrote something about prototypal inheritance but it seems that not everyone tested correctly expected extend behaviour, that should be:

b.constructorIs(B), // TRUE

to be able to use every instance constructor to call, or apply, methods or, why not, to call or apply constructor.prototype.constructor one!

// base constructor or generic Object prototype
// to call its super (parent) while there's one
Object.prototype.SuperCall = function(method){
var parent = this.parent,
result;
if(this.parent.prototype.parent)
this.parent = this.parent.prototype.parent;
result = parent.prototype[method].apply(this, [].slice.call(arguments, 1));
this.parent = parent;
return result;
};

// base constructor
function A(){};
A.prototype.init = function(){
this.isA = true;
return this;
};

// constructor B extend A
B = $extend(A, {init:function(){
this.isB = true;
return this.SuperCall("init");
}});

// constructor C extend B
C = $extend(B, {init:function(){
this.isC = true;
return this.SuperCall("init");
}});

// A, B and C instances
var a = (new A).init(),
b = (new B).init(),
c = (new C).init();

// inheritance test
alert([
a.isA, // true
a.isB, // undefined
a.isC, // undefined
b.isA, // true
b.isB, // true
b.isC, // undefined
c.isA, // true
c.isB, // true
c.isC // true
].join("\n"));

Above example uses a parent property that should easyly be a shortcut for this.constructor.prototype.constructor.
If constructor is correctly implemented there's no reason to don't use multiple inheritance from last constructor to first extended one but this is possible only with correct extend method.

This is my humil one, self packed to stay into 205 bytes ... it's behaviour is exactly expected one and it has been used to create above example.

$extend=function(b){var p="prototype",a=[].slice.call(arguments,1),f=function(){},i=0,k;f[p]=b[p];f[p]=new f;while(i<a.length){for(k in a[i])f[p][k]=a[i][k];++i};f[p].constructor=f;f[p].parent=b;return f};


To compare extend code I've not used last example but this simplest one:

function A(){};
A.prototype.constructorIs = function(F){return this.constructor === F};

B = $extend(A);

var a = new A,
b = new B;

alert([

a.constructorIs(A), // true
b.constructorIs(B), // true
a instanceof A, // true
b instanceof A, // true
b instanceof B, // true
a instanceof B, // false
a instanceof Function, // false
b instanceof Function // false

].join("\n"));

that's exactly the same I've used to do famous frameworks/lybraries tests.
Let's start with first one?



jQuery - V 1.1.3.1
One of the best library I've ever used, intuitive and powerful but it seems that jQuery didn't understand correctly what an extend function should do.
I'm talking about $.extend one, used with more than a single argument:

function A(){};
A.prototype.constructorIs = function(F){return this.constructor === F};

B = $.extend(A,{});

var a = new A,
b = new B;

alert([

a.constructorIs(A), // true
b.constructorIs(B), // true
a instanceof A, // true
b instanceof A, // true
b instanceof B, // true
a instanceof B, // true
a instanceof Function, // false
b instanceof Function // false

].join("\n"));

quite cool John, but if I extend a constructor B starting from A, how can be an instanceof A at the same time an instanceof B???



prototype - V 1.5.1.1
This is a framework that I've never loved but I know this is a great one!
This is prototype behaviour (probably I didn't understand Object.extend usage?) :

function A(){};
A.prototype.constructorIs = function(F){return this.constructor === F};

function B(){};
B.prototype = Object.extend(B.prototype, A.prototype);

var a = new A,
b = new B;

alert([

a.constructorIs(A), // true
b.constructorIs(B), // true
a instanceof A, // true
b instanceof A, // false
b instanceof B, // true
a instanceof B, // false
a instanceof Function, // false
b instanceof Function // false

].join("\n"));

Well ... I suppose that this is not a real extend public static Object method ... please change its name!



MooTools - V 1.11
Another wonderful framework, developed by a really good stuff ... but probably old prototype framework logic isn't yet totally removed? :-)

var A = new Class({
constructorIs:function(F){
return this.constructor === F
}
}),
B = A.extend();

var a = new A,
b = new B;

alert([

a.constructorIs(A), // false
b.constructorIs(B), // false
a instanceof A, // true
b instanceof A, // true
b instanceof B, // true
a instanceof B, // false
a instanceof Function, // false
b instanceof Function // false

].join("\n"));

Ok Valerio, I know constructor is always a sort of baseMooClass one ... but there's no way to correct this strange way to inherit functions?



dojo - V 0.9
Great dojo toolkit framework, in its new beta version, is able to produce my strange code using SuperCall method with expected constructor but an inherited instance can't return false in a "instanceof Super" check, don't You agree?

function A(){};
A.prototype.constructorIs = function(F){return this.constructor === F};

B = dojo.extend(function(){}, A.prototype);

var a = new A,
b = new B;

alert([

a.constructorIs(A), // true
b.constructorIs(B), // true
a instanceof A, // true
b instanceof A, // false
b instanceof B, // true
a instanceof B, // false
a instanceof Function, // false
b instanceof Function // false

].join("\n"));

Please dojo developers confirm us that this is an expected behaviour because it seems too much strange as behaviour :P




Ok, now it's time to speak about team or developers who probably did these tests many time before me, starting from Kevin Lindsey:

// Kevin Lindsey - Article of 2006/11/12 at 17:32:30
function A(){};
A.prototype.constructorIs = function(F){return this.constructor === F};

function B(){};

KevLinDev.extend(B,A);

var a = new A,
b = new B;

alert([

a.constructorIs(A), // true
b.constructorIs(B), // true
a instanceof A, // true
b instanceof A, // true
b instanceof B, // true
a instanceof B, // false
a instanceof Function, // false
b instanceof Function // false

].join("\n"));

Well done Kevin and your old article is cool too :-)



Next developer is, obviously, Dean Edwards and his even old Base proposal:

// Base - V 1.0.2 (now serving 2.X)

var A = Base.extend({
constructorIs:function(F){
return this.constructor === F
}
}),
B = A.extend();

var a = new A,
b = new B;

alert([

a.constructorIs(A), // true
b.constructorIs(B), // true
a instanceof A, // true
b instanceof A, // true
b instanceof B, // true
a instanceof B, // false
a instanceof Function, // false
b instanceof Function // false

].join("\n"));


Last big library that "probably" recieved Douglas Crockford help is YUI!:

// YUI! -. V 2.2.2

function A(){};
A.prototype.constructorIs = function(F){return this.constructor === F};

function B(){};

YAHOO.extend(B, A);

var a = new A,
b = new B;

alert([

a.constructorIs(A), // true
b.constructorIs(B), // true
a instanceof A, // true
b instanceof A, // true
b instanceof B, // true
a instanceof B, // false
a instanceof Function, // false
b instanceof Function // false

].join("\n"));


Well done guys and that's all folks!