Posts Tagged ‘Javascript’

Code Style is not Subjective p.2

Monday, August 10th, 2009

Now for a bit about JavaScript and C-style code. First and foremost, if you are unfamiliar or think you are familiar, but really aren’t, (because of say, the welcoming C-style syntax) with JavaScript consider reading JavaScript: The Good Parts. It is a quick, light read that provides invaluable insight into the language. JS is an immensely expressive language but some of the baggage surrounding it can make it seem like little more than a toy language that is intolerable to code in and should generally be avoided at all costs. This simply isn’t true, so go on, have a read.

Update: Indentation and brace style should follow the 1TBS/OTBS style.

When programming in JS always keep in mind that JS doesn’t come with a linker, so everything is put in the global namespace unless you explicitly state not to. All program variables should be kept either within a lambda scope e.g.

(function () {
var a = ‘foo’,
     b = function () {},
     c = ‘bar’, …
)());

or within a namespace e.g.

MYPROGRAM = window.MYPROGRAM || {};
MYPROGRAM.a = ‘foo’;
MYPROGRAM.b = function b () {};
MYPROGRAM.c = ‘bar’;

-or-

MYPROGRAM = window.MYPROGRAM || {
    a: ‘foo’,
    b: function b () {},
    c: ‘bar’
};

Also, all variables should be declared with the var keyword, otherwise the JS interpreter will automatically link it to the global object. Further, unlike many other C-style languages JS does not have block scope (neither does PHP for that matter). In languages with block scope it is suggested to not declare variables until they are used. Since both PHP and JS only have functional scope, variables should be declared at the top of the function in which they are used rather than right before they are used.

Since JS provides no linkeranything can overwrite anything else, and no runtime warning or errors are provided when such an event occurs. A lot of code in JS seems to be copied and pasted from elsewhere and this code could very well have strange interactions with your programs if the original author forgot to properly declare a variable or named one of his functions the same as yours. Always encapsulate your program variables and keep them out of the global namespace. When using a namespace, keep the namespace as all caps, this is to differentiate it from other methods or functions.

Generally, when naming function or methods, the name should start with a lower case letter, unless it is a constructor function. If it is a constructor function it should start with an upper case letter. e.g.

function Foo (bar) {
   this.hello = function () {
       alert(bar);
   }
}

var myFoo = new Foo("bar");
myFoo.hello(); //"bar"

Since we are primarily dealing with web languages it is best not to mix up code style between different C-style languages such as JS and PHP, as during the course of a day you will most likely be working in many languages. In either language either of the following are acceptable:

if (foo) {
   bar;
}

if (foo)
{
   bar;
}

if (foo)
   bar;

However, the first is the preferred way of writing it. This is due to a limitation in JS. JS implicitly inserts end of statement characters on new lines. So something like:

return
{
   foo: ‘bar’
};

may look like it is returning a new object with the property foo with the value ‘bar.’ However, it is actually returning undefined and the object literal statement is never reached. What is really meant is:

return {
    foo: ‘bar’
};

Moreover, it is very easy to make a mistake such as:

if (foo)
   bar();
   baz();

which was meant to mean:

if (foo) {
   bar();
   baz();
}

but really means:

if (foo) {
   bar();
}
baz();

It is preferred that when declaring arrays or objects that their literal forms are used. So instead of:

a = new Object();
b = new Array();

write:

a = {};
b = [];

The use of whitespace is paramount when writing code. It increases legibility significantly. The suggested use of whitespace is as follows:

  • Use a space before and after any operator e.g. /, +, *, -, &&, etc.
  • Use a space after a function declaration and the beginning and end of the argument definition e.g. function foo (a, b, c) { }. This helps differentiate between a function definition and a function invocation.
  • Use a space after semi-colons

When testing for values in JS always use === or !== operators. Their == and != counterparts do not do what you might think they do and should generally be avoided.

If a function requires many arguments consider putting the arguments in an options object and passing the object into the function instead of having a long list of arguments which may or may not be optional. This alleviates the need to have to remember the order of all the arguments and allows for more verbose code, too. For example:

function foo (bar, baz, qoz, …) {};
foo(1, 2, 3);

can be rewritten as

function foo (args) {};
foo({
   bar: 1,
   qoz: 2,
   baz: 3
});

More to come later…

One Bug to Rule Them All

Friday, July 17th, 2009

Just read an interesting article about a buffer overflow bug dealing with the select method. Interestingly, the proof of concept choose the max signed 32-bit integer of 2147483647 to cause the bug. This does indeed crash IE8, but does not crash FF3.5. I didn’t personally test any other browser. What’s even more interesting about this though is that this shouldn’t overflow in JS, as all numbers in JS are internally represented as 64-bit floats. Moreover, even if JS had a 32-bit integer number that number shouldn’t overflow at all. Given that the bug deals with the select element, my suspicion is that it is a DOM related issue that is being misrepresented as an ECMA script related issue.

The danger of using frameworks that augment default objects

Monday, July 6th, 2009

After a bit of thought it occurred to me just how dangerous using frameworks, like Prototype, which augment built-in objects in large-scale production projects.  As many avid JS developers are most likely aware, the prototype inheritance model of the language allows for the extension of virtually any object in the language, user defined or otherwise. However, this can also have severe side effects if a browser developer decides to include a method of the same name as your framework of choice added to a built-in object some years back.

Take, for example, the venerable each function. Prototype extends enumerable objects such as arrays with this function in its prototype. e.g.

Array.prototype.each = function(f){

   var i = 0;

   for(; i < this.length; i = i + 1){

      f(this[i],i);

   }

}

Now imagine this code is called in thousands of places in your project. Let’s also assume before extending the object prototype such a library would also check for the existence of said function as to not overwrite default behavior e.g.:

if(!Array.each) {

   …

}

Now imagine an A-grade browser’s newest version now includes the each method on collection objects by default but with either an argument omitted, or the argument order swapped. All of the code that you had written that depended on the augmented each function instead of the built-in each function will now no longer work in that browser, and you can’t very well force users to not use the latest version of IE, Safari, Firefox, or Opera because a decision was made some years back to use a framework which augments built-in objects.

This decision may now cost many thousands of dollars, if not more. Other JS frameworks such as YUI or jQuery seem like a much more sound choice in this respect. In my opinion, YUI is probably the better of the two choices as it is far easier to pick-up and master by developers of other, not necessarily web-centric languages. Yet in the scope of this rant, both are equally more sound than using something which could very easily and most likely inevitably break a few years down the road, costing many unnecessary man-hours, and perhaps dollars, to fix.

Internet Explorer, Tables, the DOM, and what not to do…

Saturday, March 14th, 2009

A post on reddit a few weeks ago finally inticed me enough to sign up for an account on the popular social link sharing site. The post was at a blog by Eric Vasilik (who supposedly had something to do with making IE a horrible platform to develop for) that dealt with having to insert the new rows into an existing table. Intuitively, most coders would just use the .innerHTML property and go about their day until they later test their code in IE and then everything explodes because IE doesn’t play nice when it comes to tables and .innerHTML. Especially when inserting new rows. What really irked me about the whole thing though was the way Mr. Vasilik was going about solving his problem.

For reasons beyond me Mr. Vasilik decided that it would be best to not just use the DOM methods and insert rows in that manner. Instead Eric wrapped his newly created HTML for table rows inside of <table> and <tbody> tags, put them into a hidden element on the page, and then looped over rows in the currently visible table calling the replaceChild method on the table in order to swap out the rows. This solution does, in fact swap out all of the rows. Unfortunately it is painfully slow.  Having to use the DOM alone for this is already significantly slower than using innerHTML (but IE makes this impossible, at least for versions 7 and lower) but using both innerHTML and the DOM is even slower than that. Instead of creating the table rows as strings Mr. Vasilik should have JSON encoded the tables so that he would’ve had them as objects after a quick eval or if he had to create them in the code then he should’ve used the DOM to create the objects and used insertRow, insertCell, etc. on the table object instead of using the least efficient approach he could think of.

I ended up writing a few speed tests to test innerHTML, DOM, DOM/appendChild, and DOM/innerHTML/replaceChild which can be found here (needs Firebug to run).  Not surprisingly, using innerHTML to create a new table, then calling replaceChild to swap table rows was the slowest of the tests. Full code below:

(function()

{

var table = document.getElementById(‘test’);

var tr, td;
<pre id="line1">  console.time("DOM Manipulation");
  for(var i = 0; i &lt; 1000; i++)
  {
      tr = table.insertRow(-1);
      td = tr.insertCell(-1);
      td.innerHTML = "Test";
  }
  console.timeEnd("DOM Manipulation");
  table.innerHTML = "";
  console.time("innerHTML");
  var rows = "";
  for(var i = 0; i &lt; 1000; i++)
  {
     rows += "&lt;tr&gt;&lt;td&gt;test&lt;/td&gt;&lt;/tr&gt;";
  }
  table.innerHTML = rows;
  console.timeEnd("innerHTML");
  table.innerHTML = "";
  console.time("DOM, appendChild");
  for(var i = 0; i &lt; 1000; i++)
  {
    tr = document.createElement("TR");
    td = document.createElement("TD");
    td.innerHTML = "Test";
    tr.appendChild(td);
    table.appendChild(tr);
  }

  console.timeEnd("DOM, appendChild");
  rows = "&lt;table id=’test2′&gt;";
  console.time("DOM, span, replaceChild");
  for(var i = 0; i &lt; 1000; i++)
  {
    rows += "&lt;tr&gt;&lt;td&gt;test DOM, span, replaceChild&lt;/td&gt;&lt;/tr&gt;";
  }
  rows += "&lt;/table&gt;";

  var spanTemp = document.getElementById("temp");
  spanTemp.innerHTML = rows;
  var varTempTableRows = spanTemp.getElementsByTagName("table")[0].getElementsByTagName("TR");
  for(var i = 0; i &lt; varTempTableRows.length; i++)
  {
     table.replaceChild(varTempTableRows[i], table.childNodes[i]);
  }
  console.timeEnd("DOM, span, replaceChild");
})();

UPDATE: Seems like this completely changed in FF3.5 and using innerHTML is significantly faster than any of the previous methods.