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

Saturday, June 21, 2008

Lazy developers, Stack concept, and the fastest, unobtrusive, JavaScript StringBuilder

Fast Web, and lazy developers


About 1 month ago, I have posted a little piece of code that, in some way, could be a little revolution for JavaScript 3rd edition, and every kind of library.
I explained how to use native Array methods with an object, to extend Array itself, or simply create a Stack behaviour, compatible with every used browser.
Few comments a part, in both Ajaxian and this site, it seems that nobody had enough fantasy to use that tricky piece of code to create every kind of Stack based constructor, library, or Iterator, without destroying the native Array.prototype. So, here I am, with another little piece of code that uses the same Stack concept to create a StringBuilder constructor.

The Fastest Unobtrusive JavaScript StringBuilder


A common problem with JavaScript and, specially, Internet Explorer, is string concatenation.
You can find every kind of benchmark, even in IEBlog, about string concatenation problems.
IE Team seems to suggest the usage of an Array, because it is much faster than plus operator, i.e.

"a" + "b" + "c"
// slower than
["a", "b", "c"].join("")

Different libraries, and different developers, use daily a StringBuilder like constructor, to perform string concatenation and finally create resulted string.

You can find different implementation, and someone is better for some feature than another one, but every implementation is not using the Stack constructor trick:

function StringBuilder(){
// (C) Andrea Giammarchi - Mit Style License
if(arguments.length)
this.append.apply(this, arguments);
};
StringBuilder.prototype = function(){
var join = Array.prototype.join,
slice = Array.prototype.slice,
RegExp = /\{(\d+)\}/g,
toString = function(){return join.call(this, "")};
return {
constructor:StringBuilder,
length:0,
append:Array.prototype.push,
appendFormat:function(String){
var i = 0, args = slice.call(arguments, 1);
this.append(RegExp.test(String) ?
String.replace(RegExp, function(String, i){return args[i]}):
String.replace(/\?/g, function(){return args[i++]})
);
return this;
},
size:function(){return this.toString().length},
toString:toString,
valueOf:toString
}
}();

Above constructor is fast as is Array one, when you use them to push values inside the stack. In few words, there's nothing faster to do this simple task, than a native method as push is.

var str = new StringBuilder("a", "b", "c");
str.append("de", "f");
alert(str.length); // 5
alert(str.size()); // 6, the length of the string
alert(str); // abcdef

You can use a StringBuilder instance inside append method without problems, as you can use plus operator to simply create a String, even if it does not make sense

alert(new StringBuilder("abc") + new StringBuilder("def"));
(str = new StringBuilder("abc")).append(new StringBuilder("def"));
alert(str);

Finally, you can use a simplified version of the appendFormat method too:

alert(
new StringBuilder("a", "b", "c").
appendFormat("{0}{1}{0}", 0, 1).
appendFormat("???", 1, 2, 3)
);
// abc010123


What's next?


We can use Stack and/or ArrayObject constructors, or concepts, to literally create every kind of FILO/LIFO/LILO/FIFO based constructor, and without modifying the native Array.prototype.
It is simply up to you, but what I am already working over is:

  • a generic Collection constructor, with predefined Type (new Collection(Person), to have a Collection of Persons, for example)

  • a Matrix manager, which aim is to become the fastest one, with JavaScript

  • something else ... so please, stay tuned ;)

Saturday, May 24, 2008

More standard Stack, and more slim ArrayObject

I have just updated both Stack and ArrayObject constructors.

The Stack improvement is About concat method, now truly standard, accepting arguments that are not instance of Stack or Array.

var s = new Stack(1, 2, 3);
alert(s.concat([4, 5], 6)); // 1,2,3,4,5,6


Since this method uses defined slice one, there is no more reason to redefine concat method in inherited prototypes. That is why ArrayObject does not need anymore concat method, for a total of 4 fast redefined methods, plus inherited concat.

ArrayObject is now the fastest extended Array constructor that returns instances of the same constructor, ArrayObject.

ArrayObject is, at the same time, the base to create every other kind of cool library, using native Array methods power.

Enjoy!

Thursday, May 22, 2008

Stack and ArrayObject - How to create an advanced subclassed Array constructor

Another problem left to solve is that after subclassing an Array one would like the return types of Array.prototype methods to be of the subclass type instead of its superclass type Array.

This comment is, basically, a summary of the reason I created the ArrayObject constructor.

Today, I have totally rewrote that constructor, using a Stack instance as prototype.

The Stack constructor aim is to subclass the Array one in the most compatible, and light, way.

For this reason, I have not changed native Array behaviours, those that return an Array, for example, instead of a Stack instance (concat, filter, map, slice).

But the good thing of Stack, is that now we can create our subclassed Array constructor, without affecting the native Array object, and adding, modifying, or removing, whatever we want.

For example, to solve the problem described on top of this post, I have simply modified inherited Stack methods, returning an instance of ArrayObject, every time we use, for example, a concat.

var a = new ArrayObject(1,2,3),
b = new ArrayObject(7,8,9),
c = a.concat([4,5,6], b);
alert(c); // 1,2,3,4,5,6,7,8,9
alert(c instanceof ArrayObject); // true

The same behaviour is obtained using slice, map, or filter, so we have our constructor, with our prototype, loads of possibilities.

The valueOf revenge


In ArrayObject, I have changed valueOf behaviour. This method returns basically the same information of toString, but it is used internally in a lot of cases.
One of them, is when you compare, sum, multiply, divide, whatever two variables.

// how to know if an ArrayObject has more elements than other one
var a = new ArrayObject(1,2,3),
b = new ArrayObject(4);
alert(a > b); // false

Above example calls in an implicit way the valueOf prototype methods.
Since latter one returns the length of the ArrayObject, and since b is an ArrayObject with length of 4, the a variable, with only 3 elements, will fail that kind of check.
Another interesting example that could allow us to write less code is this kind of check to know if an ArrayObject has some element:

var a = new ArrayObject(),
b = new ArrayObject(1,2,3);
if(-a)
alert("a has some element");
if(-b)
alert(b); // 1,2,3

Above example shows how to use in a quick and dirty way the valueOf behaviour with an ArrayObject instance. It is the same of:

if(a.length)
alert("a has some element");
if(b.length)
alert(b); // 1,2,3


The "to" prototype and the public static create


In the old ArrayObject version, I used a to prototype to convert a variable into a different one, using a constructor.
The main purpose of the Stack constructor, is to avoid Array.prototype methods assignment, as should be for every other native constructor to avoid problems between libraries.
In this version, the to prototype is only for ArrayObject:

var a = new ArrayObject(4,5,6),
b = [1,2,3].concat(a.to(Array));

In this way we could convert an ArrayObject in every other Array like compatible instance, such Array, or Stack itself.

To invert the conversion, we could use the factory pattern via create.

var a = [1,2,3],
b = ArrayObject.create(a).reverse();
alert(b instanceof ArrayObject); // true


I am working to fix last little problems with my code, but I think both Stack and ArrayObject could be used witout problems, and starting right now ;)

Tuesday, May 20, 2008

Habemus Array ... unlocked length in IE8, subclassed Array for every browser

History


I do not know how many time, during these years, JavaScript Ninjas tried to subclass the native Array to create libraries over its powerful methods without losing performances. I have finally discovered the way to remove locked length from Internet Explorer 8, and to solve problems with every other browser.

We tried to inherit Array instead of Object


This is where my last trip started, simply looking at arguments behaviour. It was there, since 2000 when I started to code in JavaScript, and it was so simple that probably few developers thought about them!

var o = {
length:0,
push:Array.prototype.push,
toString:Array.prototype.join
};

o.push(1,2,3);
alert(o); // 1,2,3

arguments, in JavaScript, is an instanceof Object, and not an Array, as is in ActionScript since version 1.0
What we have done all this time, is to use Array.prototype methods injecting a basic object, with a simple length parameter, inside.
If an object with a length value can be used as an Array, why on heart above code should not work?

We all love prototypal inheritance, we all want an instanceof Array


If you try to inherit directly an array as prototype, Internet Explorer will fix every instance length property, destroying possibility to use simple for loop over generated values.

function MyArray(){};
MyArray.prototype = [];

var a = new MyArray;
a.push(1,2,3);
alert(a.length); // 0 with every Internet Explorer

Problems are much more than a fixed length, as I wrote many months ago when I presented my ArrayObject.
On the other hand, this kind problem has been fixed for Internet Explorer 8, and 7 emulation.
Yes, finally I did it!

/**
* Choose a name for subclassed Array
*/
Stack = (function(){ // (C) Andrea Giammarchi - Mit Style License

/**
* Your personal Array constructor
*/
function Stack(length){
if(arguments.length === 1 && typeof length === "number")
this.length = -1 < length && length === length << 1 >> 1 ? length : this.push(length);
else if(arguments.length)
this.push.apply(this, arguments);
};

// Solution 1:
// Declaration of generic function
// with an array as prototype
function Array(){};
Array.prototype = [];

// Solution 2:
// use the prototype chain to inherit
// Array constructor and its native prototype
Stack.prototype = new Array;

// Solution 3:
// overwrite inherited length with zero value
Stack.prototype.length = 0;

// Solution 4:
// redeclare toString method in this way
// to let JScript core feel better
Stack.prototype.toString = function(){
return this.slice(0).toString();
};

/**
* Return and assign subclassed Array
*/
Stack.prototype.constructor = Stack;
return Stack;

})();

Above code is the basis to create an alternative Array constructor that will be able to work as expected with every browser plus IE8, without length problems.
If you try to remove a single comma from some Solution, it will never work.
If you directly assign an array to the prototype, length will be fixed.
If you remove toString prototype, FireFox and others will not work as expected.

The definitive workaround for every browser


Since first part of this post could be used in every browser, starting from IE 5.5, these old browser can simply use a constructor with a prototype full of native methods, but without instanceof Array behavior.
At the same time, every other cool browser (Safari, Firefox, Opera) could use above code to have the same behavior of IE8.

This is the full cross browser Stack constructor

While this is an improvement over basic JS 1.5 Array, to have JS 1.7 methods too, natives with updated browsers, emulated in a fast standard way with every other.
Stack Extended JS 1.7 Subclassed Array

The last problem to solve, the concat method


concat, is as simple as truly bastard prototype!
There is no way to use native concat method, even with prototypal chain inherited native Array instances.
This is why I have normalized that method, in a Stack self compatible way.
On the other hand, you cannot send a Stack instance as concat parameter, but you can always use native slice method, fast as native one is.

Best performances ever


Yes, using native, incore, prototypes, makes your code execution faster.
This compatibility + benchmark page, can tell you more about this Stack implementation than me, specially with IE8, Safari, and Opera, where performances are neary the same of a generic Array.

FireFox is probably the one that has more problems to manage native code with dynamic constructors, but hey, I am talking about Firefox beta 3, while probably RC1 or nex release will be fast as Safari, or Opera, are.

What to do with Stack?


Libraries, libraries, and libraries, finally with core performaces, the possibility to truly extend the Array, removing every fake iframe, popup, whathever you have used during these days.

Have fun with Stack, and see you soon for some other cool example with them :geek: