Common Problems

Juho Vepsäläinen edited this page Jun 1, 2013 · 11 revisions

Even though you can plenty done with JavaScript it isn't without problems. ES6 aims to alleviate some of those. There are all sorts of solutions depending on how far you want to go.

Globals and Other Nasties

As you probably know by now all variables are global by default. You will have to use var keyword to explicitly mark them as global. To quote certain Ackbar, "it's a trap". There are a couple of ways to deal with the issue. I'll start with a conventional one. Consider the example below.

function mathMagic(items, b, c) {
    var fac = 5;
    var rx, ry, rz, ret;

    ret = items.map(function(v) {...});
}

As seen above I prefer to group my variables at top of a function by default. They might get some default values or not. There are some people that use just one var and then group the variables using that nifty , syntax. In any case it is a good convention to have.

This doesn't eliminate the issue totally. It can be very beneficial to configure your editor to highlight globals as red or some annoying color (maybe pink?).

Strict mode can be very helpful as well. It changes the behavior of the language a bit and makes it easier to spot errors such as this one. Nicholas Zakas' post on the issue likely provides some extra enlightenment.

Tools such as JSLint and its less opinionated little brother JSHint allow you to spot certain groups of problems quite easily. I recommend picking either one and hooking it up with your editor. You'll save some time and maybe develop that bald a bit later rather than sooner.

If you are really obsessed, maybe it's time to write a transpiler or use some existing one. After all, what could you not solve by writing code that writes code? That's yet another bag of problems, though. In practice simple code transformation tools can be nice, though. Just keep your cool with it.

Function Syntax Is Cumbersome

It can be a bit boring to write that function always, particularly if you enjoy using anonymous functions. Speaking of which it may be handy to name your functions even if you use them as callbacks like below:

$('.clickety').on('click', function clickety() {
    console.log('Clickety click');
});

I am leveraging jQuery selector syntax here. When an element with class "clickety" is clicked, it should print "Clickety click" to the console.

Of course this doesn't solve the original issue I was complaining about. In fact we have even more noise in the code right now. In this particular case we might be able to rewrite the function using bind using console.log.bind(console, 'Clickety click'). Even that can be optimized further. Lazy ones of us would just rewrite console.log (console.log = console.log.bind(console)) and then define partial that internally uses bind. In this case you may want to leverage arguments feature discussed at the earlier chapter.

If you are willing to accept the visual noise, you can most likely configure your editor to help you write functions faster. Ie. in vim you might want to just define abbr fn function at your .vimrc. I am certain there are some nicer solutions around as well.

sweet.js provides another solutions: macros. It allows you to replace the function keyword with def. Using sweet introduces a new dependency to your project and you will also need to compile your code to real JavaScript before you are able to use it in your browser. This isn't as big a problem as it sounds, though. You will most likely end up doing some sort of compilation pass anyway, particularly if your project is a big one. Besides trivial stuff like this macros allow you to implement way more complex features on top of the core language. Whether or not that is a good idea is debatable, though.

There might be something better in sight thanks to ES6. This something is also known as "fat arrow". As illustrated by Angus Croll, the syntax is quite simple. Consider the examples below I borrowed from his blog post:

var fat1 = () => {};
var long1 = function() {};
 
// return the square
var fat2 = x => x * x;
var long2 = function(x) {return x * x};
 
// add two numbers
var fat3 = (a, b) => a + b;
var long3 = function(a, b) {return a + b};

// return a new array containing the squares of the original...
[1, 2, 3, 4, 5].map(x => x * x); // [1, 4, 9, 16, 25]

Now try thinking what the examples above would look like in vanilla JavaScript. Yes, it would be quite verbose. Sometimes you can hide the complexity behind utility functions. bind comes in handy yet again.

This is still very bleeding edge. The specification is till work in progress. As things start to settle down I expect that people will begin to use transpilers (ES6 -> ES4) that allows them to use the subset of functionality they want. Esprima is one popular starting point for this type of work.

You can try to parse it by hand as I did in mojs but that is an approach I just cannot recommend. You will want to use something that provides AST level support and allows you to hack in the level of a language definition.

Variables and Arguments Are Undefined by Default

As you have probably noticed so far, JavaScript allows a lot, perhaps too much by default. You will likely bump into this problem when dealing with variables and function arguments. In case those are provided, they will undefined by default. Sometimes this is a good thing, sometimes not. The worst thing is that you may end up with silent failures. As such it is one possible source of errors and hard to trace issues.

You may try to introduce some rigidity to your code using tooling such as annotate (my own library) or just use a dialect, such as TypeScript, that provides extra rigidity. The exact choice depends on your needs. I am actually curious to know how you have solved the issue (or is it an issue at all).

Pyramid of Doom

Even though callbacks are powerful, they come with a price. It is very easy to end up in a situation where you have nested callbacks like in the abbreviated example by @adamghill below:

var db = require('./db/callbackDb');
 
db.set('key1', 'value1', function(err) {
    if(err) throw err;
 
    db.set('key2', 'value2', function(err) {
        if(err) throw err;
 
        db.set('key3', 'value3', function(err) {
            if(err) throw err;
 
            var str = '';
            db.get('key1', function(err, value) {
                if(err) throw err;
                
                str += value + ' - ';

                console.log(str);
            });
        });
    });
});

That doesn't look particularly nice. In fact callbacks can be considered an imperative language construct. A declarative construct such as a promise gives more indirect control and helps to cut down the amount of code. Consider the following abbreviated example (again, original work of @adamghill):

var db = require('./db/promisesDb');
var str = '';
 
db.set('key1', 'value1').then(function(key) {
    return db.set('key2', 'value2');
}).then(function() {
    return db.set('key3', 'value3');
}).then(function() {
    return db.get('key1');
}).then(function(value) {
    str += value + ' - ';
 
    console.log(str);
})

As you can see, the pyramid has been eliminated. The idea of promises is to represent a value in the future. Rather than defining when as in the imperative world, you describe then that is triggered as the value has become available. It comes with a price, though. You will have to instrument the underlying code to support the scheme.

Besides this it is possible to tame the pyramid by using custom events or control flow tools. Reorganizing the code may be beneficial as well. You can find full examples of these at @adamghill's excellent blog post on the topic.

It is still bit of a wild west out there. In addition to these approaches, there is a whole paradigm known as Functional Reactive Programming. In fact promises are just a subset of that. Presentation slides by @eamoderorubio shows how to go beyond promises. Best practices are still forming. But there are some very good solutions available already.

Not All Things Are Equal

== and === are common stumbling blocks especially for JavaScript beginners. It is important to know that == comparison coerces values whereas === does not. Just to give you an idea of what this means. Consider the following cases:

0 == '' // true
0 === '' // false

I know it might seem like a trivial detail but it is not. Used right it can be a useful feature. In worst case this behavior can introduce subtle and difficult to detect bugs.

No Operator Overloading

In case you are dealing with vector math, it can be very unfortunately that JavaScript doesn't support operator overloading out of box. This can be a powerful feature when used right. By definition it allows you to define what operations between given objects do. In case of a sum of vectors you might expect a vector. In vanilla JavaScript you would have to write a.add(b). In a language supporting overloading simply a + b would do. This can get very complicated quite fast.

Fortunately it is quite easy to write a transpiler that provides this functionality for us. I have covered how to achieve operator overloading at my blog. The solution uses a tool known as JSShaper. It is a transformation tool that gives access to JavaScript AST. In this case it is a matter of replacing operators with function calls and defining functions. This little hack allows us to overload the behavior based on types of the operands provided.

This is just one small example of how to extend the language to suit your purposes better. The main disadvantage of JSShaper is that it works only on valid JavaScript syntax. As a result you cannot implement custom language constructs as there is no way to extend AST. In case you can get away by doing some transformation within the current definition, you should be alright.

Not Invented Here

Even though it can be quite fun to write JavaScript libraries, widgets and whatnot, there are times when you are better off using some existing solution. As you probably know writing that 80% takes 20% where as the remaining 20% will suck the rest. Especially browser support can be a tough nut to solve depending on your domain. Of course if you just do not have to care, all the better.

Unfortunately Google isn't particularly useful for finding modules. Solutions have emerged for this problem. I have listed some of these below:

  • JSwiki - Community wiki focusing on the frontend.
  • JSter - Opinionated take on JSwiki data. In some ways it takes it further.
  • Bower Components - Bower component search.
  • NPM - Even though NPM focuses on Node.js there are various frontend modules there as well. In fact it works very well in conjunction with Browserify.

Conclusion

The current chapter is more of a guess based on my own experience. In case you feel I'm missing something essential, poke me at the issue tracker.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.