Object-oriented functions in JavaScript
As you may know, JavaScript is a bit of an indecisive language. It doesn't fully commit to any one paradigm, instead having features from object-oriented, procedural, and functional languages. Its mish-mash of philosophies can make it confusing to learn, but once in a while it manages to surprise me with some clever design decision.
JavaScript uses prototype-based object orientation. This means that there are no "classes," but objects can inherit from other objects. Each object is simply an associative array between string keys and properties, which can be primitives or functions (like methods in other languages). They also inherit properties from their "prototype" object, which can be dynamically updated, changing all sub-objects.
But JavaScript has another powerful feature that many object-oriented languages lack: first-class functions. This means that functions are treated like first-class citizens: you can pass them as arguments to other functions, return them, and assign them to variables. This is a powerful paradigm that OO languages like Java still don't really support.
For example, first-class functions let you do stuff like filtering an array based on some condition very easily:
myArray.filter(function(value, index) {
return (value % 2 === 0 || index > 5);
});
This filters out odd numbers with an index less than or equal to 5 without having to implement or override anything.
First-class functions are neat, but what makes JavaScript functions
really neat is that they are also objects. Functions can be declared
like primitives (i.e. without using a constructor, kind of like Strings
in Java), but they all inherit from the Function
global
object.
This means that functions have their own properties and methods. For
example, you can use myFunction.apply()
to change the
this
variable a function executes with. This is useful in
all sorts of scenarios, and also opens up a lot of other interesting
possibilities.
Because JavaScript objects are always mutable and inherit from other
objects, you can make new objects that inherit from the
Function
object and implement new properties.
Unfortunately the Function
constructor only operates in the
global scope, and is therefore not super userful, you can still add new
stuff to the Function
prototype.
For example, if we wanted to implement function currying we could do it by
adding a new method to the Function
prototype.
Function.prototype.curry = function(...args1) {
return (...args2) => this(...args1, ...args2);
};
We use spread syntax and rest parameters to support partial application with multiple arguments at a time. We also use an arrow function to fit the whole body in one line.
Let's test it out:
function sum3(a, b, c) {
return a + b + c;
}
sum3(1, 2, 3); // 6
sum3.curry(3)(5, 2); // 10
sum3.curry(1, 5)(6); // 12
sum3.curry(1); // Function
Now in practice you should avoid changing the prototype of any global object, but the ability to extend the Function object is a very powerful feature, and shows that despite all its weirdness JavaScript does have some good design choices.