The Javascript Functions Which Are Not

2012-06-03 15:25

It would be quite far-fetched to call JavaScript a functional language, for it lacks many more sophisticated features from the FP paradigm – like tail recursion or automatic currying. This puts it on par with many similar languages which incorporate just enough of FP to make it useful but not as much as to blur their fundamental, imperative nature (and confuse programmers in the process). C++, Python or Ruby are a few examples, and on the surface JavaScript seems to place itself in the same region as well.

Except that it doesn’t. The numerous different purposes that JavaScript code uses functions makes it very distinct, even though the functions themselves are of very typical sort, found in almost all imperative languages. Learning to recognize those different roles and the real meaning of function keyword is essential to becoming an effective JS coder.

So, let’s look into them one by one and see what the function might really mean.

A scope

If you’ve seen few good JavaScript libraries, you have surely stumbled upon the following idiom:

  1. /* WonderfulThing.js
  2.  * A real-time, HTML5-enabled, MVC-compatible boilerplate
  3.  * for appifying robust prototypes... or something
  4.  */
  5.  
  6. (function() {
  7.     // actual code goes here
  8. })();

Any and all code is enclosed within an anonymous function. It’s not even stored in a variable; it’s just called immediately so its content is just executed, now.

This round-trip may easily be thought as if doing absolutely nothing but there is an important reason for keeping it that way. The point is that JavaScript has just one global object (window in case of web browsers) which is a fragile namespace, easily polluted by defining things directly at the script level.

We can prevent that by using “bracketing” technique presented above, and putting everything inside this big, anonymous function. It works because JavaScript has function scope and it’s the only type of non-global scope available to the programmer.

A module

So in the example above, the function is used to confine script’s code and all the symbols it defines. But sometimes we obviously want to let some things through, while restricting access to some others – a concept known as encapsulation and exposing an interface.

Perhaps unsurprisingly, in JavaScript this is also done with the help of a function:

  1. var counter = (function() {
  2.     var value = 0;
  3.     return {
  4.         increment: function(by) {
  5.             value += by || 1;
  6.         },
  7.         getValue: function() {
  8.             return value;
  9.         },
  10.     };
  11. })();

What we get here is normal JS object but it should be thought of more like a module. It offers some public interface in the form of increment and getValue functions. But underneath, it also has some internal data stored within a closure: the value variable. If you know few things about C or C++, you can easily see parallels with header files (.h, .hpp, …) which store declarations that are only implemented in the code files (.c, .cpp).

Or, alternatively, you may draw analogies to C# or Java with their public and private (or protected) members of a class. Incidentally, this leads us to another point…

Object factories (constructors)

Let’s assume that the counter object from the example above is practical enough to be useful in more than one place (a tall order, I know). The DRY principle of course prohibits blatant duplication of code such as this, so we’d like to make the piece more reusable.

Here’s how we typically tackle this problem – still using only vanilla functions:

  1. var createCounter = function(initial) {
  2.     var value = initial || 0;
  3.     return {
  4.         increment: function(by) {
  5.             value += by || 1;
  6.         },
  7.         getValue: function() {
  8.             return value;
  9.         },
  10.     };
  11. };
  12. var counter = createCounter();
  13. var counterFrom1000 = createCounter(1000);

Pretty straightforward, right? Instead of calling the function on a spot, we keep it around and use to create multiple objects. Hence the function becomes a constructor for them, while the whole mechanism is nothing else but a foundation for object-oriented programming.

\displaystyle functions + closures = OOP

We have now covered most (if not all) roles that functions play when it comes to structuring JavaScript code. What remains is to recognize how they interplay with each other to control the execution path of a program. Given the highly asynchronous nature of JavaScript (on both client and server side), it’s totally expected that we will see a lot of functions in any typical JS code.

Callbacks

Many of them – likely most – are callbacks to be invoked when some asynchronous operation has finished. Whether it’s an AJAX request in the browser or an I/O call done by node.js on the server, it’s through callback function that we are notified of its completion. The result, if any, is therefore passed as parameter:

  1. var fs = require('fs');
  2.  
  3. fs.readFile('file.txt', 'ascii', function(err, data) {
  4.   if(err) {
  5.     console.error("Could not open file: %s", err);
  6.     process.exit(1);
  7.   }
  8.   console.log(data);
  9. });

Written this way, the asynchronous code reads almost exactly like the synchronous one. However, in general this ceases to be true for any more complicated cases, such as issuing more than one simultaneous call and requiring for any/all of them to complete. And even for non-parallel, sequential algorithms we may quickly encounter the problem of “marching callbacks”, resulting in what is often called a callback spaghetti:

  1. // from: https://github.com/koush/node/wiki/%22async%22-support-in-node.js
  2. var db = require('somedatabaseprovider');
  3. app.get('/price', function(req, res) {
  4.   db.openConnection('host', 12345, function(err, conn) {
  5.     conn.query('select * from products where id=?',
  6.                [req.param('product')], function(err, results) {
  7.       conn.close();
  8.       res.send(results[0]);
  9.     });
  10.   });
  11. });

Trying to alleviate that problem by using two-space indents (rather than more sensible four-space ones) doesn’t seem like a satisfactory solution, though. Fortunately, there are some better ways to structure asynchronous computation.

Pipelines

For an example, look at execution patterns implemented by the async.js library. The one that directly addresses the callback problem is either async.series or async.waterfall:

  1. async.waterfall([
  2.     function(next) {
  3.         next(null, 0);
  4.     },
  5.     function(res, next) {
  6.         next(null, res + 1);
  7.     },
  8.     function(res, next) {
  9.         next(null, res + 2);
  10.     }
  11. ],
  12. function(err, res) {
  13.     console.log(res); // 3
  14. });

It does so by forming a kind of asynchronous pipeline whose elements are, of course, functions of particular form. Besides getting the current result as argument, those functions also receive a continuation (here, next) which is called when we’re ready to advance to the next element in the pipeline. Naturally, this continuation is also a function – a special one prepared by async.waterfall routine.

Functions, functions…

So indeed, there is usually plethora of functions littering any non-trivial JavaScript code. Moreover, the sheer amount of different constructs they try to emulate is a tricky gotcha that not many programmers seem to be aware of.

In this post I tried to outline the most common design patterns in JavaScript that involve a clever use of plain ol’ functions. I’m pretty sure, though, that there are still many which I simply haven’t encountered yet. Hence I advise everyone working with JavaScript to pay close attention to them. Quite often it’s not really the function that the function is about.

Tags: , , , , ,
Author: Xion, posted under Programming »



Adding comments is disabled.

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.