JavaScript Day 6: WireLibJS internals

Today I thought it would be interesting to take a look into the architecture of how WireLibJS is built. It may look a bit odd at first, so it’s worth going through it to understand a bit more of the JavaScript way of doing things. Again, I’m no expert in this field, just trying to put into practice the stuff that I’ve learned recently.

Here’s a direct link to the file so you can follow along: http://www.bit-101.com/jscanvas/wirelib.js

First let’s look at the first and last lines, which wrap everything else:

[php lang=”JavaScript”]var wirelib = (function() {

}());[/php]

Here, we are creating a var called wirelib and assigning … something … to it. At first, it looks like we are assigning a function to it, but that’s not quite it. We’re assigning the result of running an anonymous function to the wirelib var. In JS, functions are created pretty much the same way as in AS1-3. We have named functions and anonymous functions:

[php lang=”JavaScript”]function foo() {

}

function() {

}[/php]

The first creates a function named foo, the second creates an anonymous function. unless you assign that function to a variable to reference it by, there will be no way to do anything with it. As I said the other day, anonymous functions are generally looked down upon in ActionScript, but they can be very useful in JavaScript. Remember that the cardinal sin in JS is polluting the global namespace. Any vars or named functions you put in the top level of a .js file that gets included in a web page will become members of the window object of that page. Not good manners. But vars and functions defined WITHIN another function are in the local scope of that function. They do not exist outside of the scope of the function and will not pollute the global namespace. So they become a great utility for keeping things clean and compartmentalizing code.

However, if you name that function, then the function itself becomes part of the global namespace. But an anonymous function avoids even this. But, you say, how can we do anything with an anonymous function if it doesn’t have a name and it isn’t assigned to a reference? It goes away the second the line of code it is created on is through. Well, we have to execute it BEFORE that line of code is through. So we create it and execute it in one sweep, before it goes away. There are two ways to do that.

[php lang=”JavaScript”](function() {

})();[/php]

[php lang=”JavaScript”](function() {

}());[/php]

Very subtle difference there. One encloses the whole function in parentheses and then executes the function by calling it with another pair of parentheses. The other creates the function and then calls it, wrapping the whole thing in parentheses. Apparently the outer wrapping parens are still needed in the second method.

If this looks confusing to you, think of it like this:

[php lang=”JavaScript”]function init() {

}
init();[/php]

or

[php lang=”JavaScript”]var init = function() {

}
init();[/php]

Although slightly different, both end up with something called init that is a reference to a function, which is then called. All we are doing is removing the variable name and shrinking it down to one line.

In the case of WireLibJS, we are expecting the execution of this anonymous function to return something that we are then assigning to this wirelib variable. Realize that this variable, wirelib, IS created in the global namespace. I didn’t say it’s forbidden to create stuff there, just that you shouldn’t pollute it. You obviously need to put something there so that your scripts can access your library. But your library should strictly limit what it places there, and clearly document it. I’m creating a single variable called wirelib. It’s value is assigned by the return value of this anon function, which we’ll see shortly.

OK, so what’s in this function?

First we create some vars:

[php lang=”JavaScript”]var canvas, context, width, height, lines = [], cx, cy, cz, fl = 250, interval, loopCallback, running;[/php]

Remember, these are local vars to the function, so they will only exist within the scope of the function. We are safe here. After defining the vars, a whole bunch of functions are defined. Like the vars, these functions are created within the local namespace internal to the function. They do not exist outside. Also, because they are in the same scope as the vars, they can access those vars.

Finally, the anonymous function returns an object that contains a reference to a bunch of those external functions and a few plain values:

[php lang=”JavaScript”] return {initWithCanvas:initWithCanvas,
addLine:addLine,
addBox:addBox,
addRect:addRect,
addCircle:addCircle,
draw:draw,
rotateX:rotateX,
rotateY:rotateY,
rotateZ:rotateZ,
translate:translate,
jitter:jitter,
clearCanvas:true,
strokeStyle:”#000000″,
lineWidth:1,
loop:loop,
stop:stop,
showCenter:false,
setCenter:setCenter
};[/php]

Another odd looking construct, but it’s just a plain generic object with a property called initWithCanvas that is assigned a reference to the initWithCanvas function defined above, a property called addLine that’s assigned a reference to the addLine function, etc. It is this object that gets returned to the global wirelib object and in effect becomes the WireLibJS library that you interact with.

Some interesting things take place here with the magic of scope and closures. Note that this wirelib object ONLY contains those properties we assigned to it in that last line, mostly functions. However, those functions, being in the same scope as that long list of vars defined up at the top, have access to those vars. Even after the anonymous function completes its execution and goes out of scope and dies, though, those functions can still access those local vars. Read up on “closures” to understand this bit of magic. However, those vars are not accessible from any outside code. If you want to think about it in object oriented terms, the function is somewhat like a class. Anything assigned to that object it returns is a public function or variable. The local vars wind up working sort of like private vars. The functions can manipulate them, but nothing outside can.

With this setup, we can now say wirelib.initWithCanvas($(“#canvas”)[0]), running this function:

[php lang=”JavaScript”] function initWithCanvas(aCanvas) {
canvas = aCanvas;
if(canvas !== undefined) {
width = canvas.width;
height = canvas.height;
cx = width / 2;
cy = height / 2;
cz = fl * 2;
context = canvas.getContext(“2d”);
}
}[/php]

This assigns the canvas to the local canvas var, then figures out the values to width, height, cx, cy, cz, and context, all other local vars. These vars are then used by several of the other functions.

With this pattern, we get many of the same concepts that we would get with a class-based, object oriented language: objects with functions and private internal data. I’m not saying it IS a class, or even that it is LIKE a class, but it gives some of the same benefits of using a class. Also, you could say that it is sort of a singleton. There is only ever a single instance of it, globally accessible, and no way to create more.

The way I’ve done this is only one of the possible patterns of doing this kind of thing. There are certainly ways of creating non-singleton objects, and even very different ways of doing pretty much what I’ve done here. Again, I can’t recommend the JavaScript Patterns book enough. It’s really quite awesome, not only in presenting all these options and patterns, but in really explaining why you do things certain ways, the benefits and drawbacks to each method, etc.

Get this book! It rocks!

OK, that’s it for today. Tomorrow… Hell, I don’t know yet, but I’ll think of something.

This entry was posted in JavaScript. Bookmark the permalink.

2 Responses to JavaScript Day 6: WireLibJS internals

  1. tlecoz says:

    Thanks a lot for all these tutorials, it help me a lot to start with JS ( I use to be a flash developer).

    Actually, it ‘s possible to create a “real-fake” class in JS with variables & cie. I never did a JS project, but I did some JSFL tools and I coded my objects like that

    test = new Object();
    test.color = “#00ff00”;
    test.canvas = null;
    test.context = null;

    test.initCanvas = function(canvas){
    this.canvas = canvas;
    this.context = canvas.getContext(“2d”);
    }
    test.drawSquare = function(){
    this.context.fillStyle = this.color;
    this.context.fillRect(0,0,200,200);
    }

    I just test it in a web page and it works as expected 🙂

    I discover yesterday that it’s also possible to use real getter and setter with JS (but it doesn’t work on IE8 , I dont know about IE9 , I’m on WinXP).

    The code is a little bit weird but simple :

    test.prototype = {
    get color(){
    return this.color;
    }
    ,
    set color(colorStr){
    this.color = colorStr;
    }
    }

    In the Main JS, you can use it as expected :

    test.initCanvas(canvas);
    test.color = “#00ff00”;
    test.drawSquare();

    Sorry for the long speech, and thanks again for all the work you share with us.

  2. tlecoz says:

    Hello again,

    Excuse me , but I was certain that I already coded some JSFL with real objet structure, and I finally find my old code (sorry for the multi-post, you can delete my first comment )

    The code looks like that :

    Rectangle = function(){
    _color = “#00ff00”;
    _x = 0;
    _y = 0;

    _canvas = null;
    context = null;

    }

    Rectangle.prototype.__defineGetter__(“canvas”, function(){ return _canvas; });
    Rectangle.prototype.__defineSetter__(“canvas”, function(n){
    _canvas = n;
    context = _canvas.getContext(“2d”);
    });

    Rectangle.prototype.__defineGetter__(“color”, function(){ return _color; });
    Rectangle.prototype.__defineSetter__(“color”, function(n){ _color = n; });

    Rectangle.prototype.__defineGetter__(“x”, function(){ return _x; });
    Rectangle.prototype.__defineSetter__(“x”, function(n){ _x = n; });

    Rectangle.prototype.__defineGetter__(“y”, function(){ return _y; });
    Rectangle.prototype.__defineSetter__(“y”, function(n){ _y = n; });

    Rectangle.prototype.draw = function(){
    context.fillStyle = _color;
    context.fillRect(_x,_y,200,200);
    }

    you can create an instance like that

    var rect = new Rectangle();
    rect.color = “#ff0000”;
    rect.x = 100;
    rect.y = 100;
    rect.canvas = canvas;
    rect.draw();

    And you can extends the object like that

    RectangleChild.prototype = new Rectangle();
    RectangleChild.prototype.constructor = RectangleChild;
    function RectangleChild(){
    _width = 200;
    _height = 200;
    }

    RectangleChild.prototype.__defineGetter__(“width”, function(){ return _width; });
    RectangleChild.prototype.__defineSetter__(“width”, function(n){ _width = n; });

    RectangleChild.prototype.__defineGetter__(“height”, function(){ return _height; });
    RectangleChild.prototype.__defineSetter__(“height”, function(n){ _height = n; });

    RectangleChild.prototype.draw = function(){
    context.fillStyle = _color;
    context.fillRect(_x,_y,_width,_height);
    }

    then, you can create an instance in the same way

    var rect = new RectangleChild();
    rect.color = “#ff0000”;
    rect.x = 100;
    rect.y = 100;
    rect.canvas = canvas;
    rect.draw();

    I’m not really sure why I’m explaining all these stuff XD
    You probably know that very well…

Leave a Reply