Why JavaScript Is Awesome

YES! THATS AWESOME

Over the years, JavaScript has changed tremendously and mostly for the better. But the fundamentals have remained.

One of the things I love about JavaScript is being able to pass around a function with no signature, yet being able to handle any number of arguments being passed in.

Only an example will make this clear and why it's so much more flexible than statically compiled languages.

In C#, you create a function like so:

public int function add(int arg1, int arg2) {  
  return arg1 + arg2;
}

Or if you wanted to pass functions around like objects in C# you can do this:

Func<int, int, int> fnAdd = (x, y) => x + y;  
Func<int, int, int> fnSubtract = (x, y) => x - y;  
fnAdd(2,2); // 4  
fnSubtract(2,2); // 0  

Then you can use these functions as objects to drive dynamic behavior like this:

public void function ProcessNumbers(int x, int y, Func<int, int, int> fnOperation) {  
  if (fnOperation == null) throw new ArgumentNullException("fnOperation");

  Console.WriteLine(x);
  Console.WriteLine(y);

  var result = fnOperation(x, y);
  SaveResultToDatabase(result);
}

ProcessNumbers(1, 2, fnAdd);  
ProcessNumbers(3, 3, fnSubtract);  

So as you can see, we are encapsulating the part that varies, or the actual operation on the numbers. The reason we want to do this is so we don't have to repeat ourselves. We don't want to end up with something like this:

public int function AddNumbers(int x, int y) {  
  Console.WriteLine(x);
  Console.WriteLine(y);

  var result = x + y;
  SaveResultToDatabase(result);
}

public int function SubtractNumbers(int x, int y) {  
  Console.WriteLine(x);
  Console.WriteLine(y);

  var result = x - y;
  SaveResultToDatabase(result);
}

AddNumbers(1, 2);  
SubtractNumbers(3, 3);  

This is bad because look at all the code that's duplicated. If you needed to change the flow of this function, say you didn't want to log the numbers anymore or you wanted the logging statements to be a little different, you'd have to change the code in both places.

For this example, it would be simple enough to modify these two little functions, but in the real world it will get very complicated very quickly. What if these functions were 100 lines instead of 4? 100 lines is probably already way too big in my book, but you will run into functions that are that big or much bigger in the wild all the time.

Then you'd have 100 lines of duplicated code that need to stay in sync when they change to remain correct. And what if instead of 2 functions with the duplicated code, we had 100? Then you have potentially 100 places that would need updating.

It may sound far-fetched, but believe me, I've seen it in production...
Anytime you're copying and pasting code it's a sign that something could be designed better.

So our first pattern in C# where we pass around the function is good. We have a way to plugin different functionality without duplicating the rest that is the same.

And if the SaveResultToDatabase function needed to vary based on what type of operation you're doing (addition or subtraction) then we can encapsulate that function as well:

public void function ProcessNumbers(int x, int y, Func<int, int, int> fnOperation, Action<int> fnSave) {  
  if (fnOperation == null) throw new ArgumentNullException("fnOperation");
  if (fnSave == null) throw new ArgumentNullException("fnSave");

  Console.WriteLine(x);
  Console.WriteLine(y);

  var result = fnOperation(x, y);
  fnSave(result);
}

Action<int> saveAddition = (int x) => Console.Write(x);  
Action<int> saveSubtraction = (int x) => Console.Write(x);

ProcessNumbers(1, 2, fnAdd, saveAddition);  
ProcessNumbers(3, 3, fnSubtract, saveSubtraction);  

The difference between a Func and an Action is that a Func has a return value and Action doesn't. So Func means take two integer arguments and return an integer. Action means take one integer argument and since it's an Action it returns nothing. Just like writing:

public void function saveAddition(int x) {  
...
}

But what we are really talking about here are higher order functions, which is basically just a function that takes other functions as arguments. And now you know one of the big reasons that this is useful. To avoid code duplication.

So how is this related to JavaScript?

Well, you've seen how to do it in a compiled language like C# and it works all well and good. But programmers are a very lazy lot.

I don't really like writing things like:

function ProcessNumbers(int x, int y, Func<int, int, int> fnOperation, Action<int> fnSave) {  

Those Func and Action variables are pretty nasty to look at. But even aside from that, there are serious drawbacks to the C# approach.

What if I wanted a higher order function like ProcessNumbers, that could take 3 numbers instead of 2? Or 10? Or any number? I could pass in an array of numbers and then I would have to change the signature of my Func and Action variables.

Worse than that, what if I want to handle not only int but double and decimal as well?
C# does have a facility for this called Generics. While this is a powerful mechanism, it's a little confusing for the layman and not the cleanest thing to code.

What I find interesting about Javascript is that I can do this:

function fnAdd(x, y) {  
  return x + y;
}

Since it's a dynamic language that's interpreted, we don't need to give these arguments types like int. In fact, in JavaScript, there is only one number type called Number, but that's another story.

The point is, if x and y have the + operator, then this function will just work. So it will work on numbers, it will actually work on strings (as a concatenation). If we did something like x.doSomething() then as long as what we pass in has a function called doSomething, then it will work.

By not having to specify the types in my function signature, I get the flexibility in a higher order function of simply saying:

function ProcessNumbers(x, y, fnOperation, fnSave) {  
  var result;

  if (fnOperation == null) throw "fnOperation";
  if (fnSave == null) throw "fnSave";

  console.log(x);
  console.log(y);

  result = fnOperation(x, y);
  fnSave(result);
}

Notice in my function signature I don't have to specify Func fnOperation. I can pass in any function and execute that function with any arguments and as long as the function can work correctly with those arguments then all is well.

The other nice thing about JavaScript is that you can use the arguments collection that gets passed into each function:

function fnAdd() {  
  // arguments is not a true Array, this will convert it
  var args = Array.prototype.slice.call(arguments);

  var result = 0;
  args.forEach(function(arg) {
    result += arg;
  });

  return result;
}

This will allow you to do things like fnAdd(1, 2) or fnAdd(3, 4, 5, 6 7) and they will all work, without having to wrap them up in an array.

The above fnAdd function can be further reduced to:

function fnAdd(x, y) {  
  return x + y;
}
function fnSubtract(x, y) {  
  return x - y;
}

function fnProcess() {  
  var args, fn, result, numbers;

  args = Array.prototype.slice.call(arguments);
  if (args == null || args.length < 3) throw 'Invalid arguments in fnProcess';

  fn = args.slice(0, 1); // first arg is function
  result = args.slice(1, 2); // initialize result to the first passed in number
  console.log(result);

  numbers = args.slice(2); // get the rest of the numbers to process
  numbers.forEach(function(number) {
    result = fn(result, number);
    console.log(number);
  });

  return result;
}

And now you can do:  
fnProcess(fnAdd, 5, 10, 15, 20); // 50  
fnProcess(fnSubtract, 20, 5); // 15  

If you're working on a simple application, then none of this is necessary. If the code is small and trivial it's probably manageable with some duplication.

But on large-scale projects, this kind of encapsulation can save you from a nightmare of code duplication and bugs and generally make your life easier in the long-term.

We have seen that we can have a pretty flexible design in statically compiled language like C# or in a dynamic language like JavaScript. I find the JavaScript approach much more flexible and easy to work with and that's one of the reasons I think it's awesome.

If you found this article to be helpful, then you should subscribe here.

Subscribe to our mailing list

* indicates required
Subscribe
X