Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

JavaScript for C# developers: callbacks (part I)

DZone's Guide to

JavaScript for C# developers: callbacks (part I)

· Web Dev Zone
Free Resource

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

As I’ve said pretty much from the very start of this series: functions are objects. You can pass them around in variables, pass them into functions as parameters, return them from functions, the whole nine yards. When you pass a function into another where it will be called, it’s generally known as a callback. (Note: as usual I’m using Firebug in Firefox to run these examples. All browsers have similar development tools built-in or that are available as a download.)

Here’s a simple example. There’s nothing too complicated going on here: all we’re doing is passing a callback into the function that’ll get called once the function’s real work is done.

var printDone = function () {
  console.log("done");
};

var doSomeWork = function (whenDone) {
  // do some work here
  whenDone();
};

doSomeWork(printDone);

So, we’re declaring a function doSomeWork that takes a callback called whenDone as parameter. Once the function has completed, it calls the callback. In the statement at the end, I’m calling doSomeWork passing in the printDone function.

Now, I totally agree that this is a somewhat daft example. After all I could have just added a call to printDone after the call to doSomeWork, but at least it shows the basics. Many times, if not most times, developers won’t formally give the callback function a name like I just did, but instead use an anonymous function declared right there in the call.

var doSomeWork = function (whenDone) {
  // do some work here
  whenDone();
};

doSomeWork(function () {
  console.log("done");
});

Exactly the same effect, but this time we declare the function anonymously as a function literal. You should get used to this style: it’s used universally in all kinds of code.

Now that we’re used to how to declare and use callbacks, let’s up the ante a little. We’re going to write a map method for arrays. A map is a function that applies some kind of operation to each element of an array and returns the resulting values as another array. First of all, let’s write some code that’s specific but works, and then refactor it to make it more general. We’ll start off by writing a function that doubles every element in an array (which we’ll assume to contain just numbers) and returns the resulting array.

var doubler = function (a) {
  var i;
  var result = [];
  for (i = 0; i < a.length; i++) {
    result[i] = 2 * a[i];
  }
  return result;
};

var myArray = [2, 3, 5, 7, 11, 13];
console.log(myArray);

var newArray = doubler(myArray);
console.log(newArray);

Now we’ll refactor it into a method on all arrays. (Note: the JavaScript version in Firefox 4 already has a map method defined on arrays, so I’m calling mine —unimaginatively, perhaps – mapp().)

Array.prototype.mapp = function () {
  var i;
  var result = [];
  for (i = 0; i < this.length; i++) {
    result[i] = 2 * this[i];
  }
  return result;
};

var newArray = myArray.mapp();
console.log(newArray);

Notice that the call to mapp() uses the method pattern of function invocation, and therefore the this variable inside the method points to the array it was called on.

Now, for a generic map method, this is still very specific to doubling elements. But consider the code: we have the stuff that iterates through the array and we have the statement that gets called for each element. It’s this latter part we want to genericize. So, create a special local (nested) function that does the work:

Array.prototype.mapp = function () {
  var i;
  var result = [];

  var process = function (element) {
    return 2 * element;
  };

  for (i = 0; i < this.length; i++) {
    result[i] = process(this[i]);
  }
  return result;
};

And now we see that we can promote this local function into a parameter of the outer function: that is, a callback. And it gives us the form of that callback too: it takes one parameter and returns a new value.

Array.prototype.mapp = function (process) {
  var i;
  var result = [];
  for (i = 0; i < this.length; i++) {
    result[i] = process(this[i]);
  }
  return result;
};

var newArray = myArray.mapp(function (element) {
  return 2 * element;
});
console.log(newArray);

With this last step, the introduction of the callback, the mapp method has become general and we can use it for other transformations or mappings. Supply a callback that does the work, and invoke the mapp method.

var newArray = myArray.mapp(function (element) {
  return "<" + element.toString() + ">";
});
console.log(newArray);

Of course, now that we have a generic mapp() method we can add sanity checks, start to optimize it, or maybe alter the signature of the callback function we accept. For now, let’s make the callback function not only get called with the element in question, but also its index. I also want to call it in such a way that we can pass in the value that the this variable inside the callback should point to.

Array.prototype.mapp = function (process, context) {
  var i, 
      result = [],
      last = this.length;
  for (i = 0; i < last; i++) {
    result[i] = process.call(context, this[i], i);
  }
  return result;
};

var newArray = myArray.mapp(function (element, index) {
  return "<" + index.toString() + ": " + element.toString() + ">";
});
console.log(newArray);

A quick note on the code is in order. I’m calling mapp() without a second parameter. JavaScript will then set the context parameter to undefined. If context is null/undefined, it will default to the global object in the call to call(). (Recall the call method calls the function and sets its this variable to the first parameter.) Also I’m moving the repeated access to the length property out of the loop and saved its value to a local variable: it’s slightly faster.

Next time, we’ll make a slightly different version of the mapp() method to continue our exploration of callbacks.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:

Published at DZone with permission of Julian Bucknall, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}