JavaScript is one of the most popular programming languages in the world. It can do a lot and have some features that are ahead of many other languages.
In this article, we’ll look at ways to curry functions, memoization, and inheritance.
Curry
Curry functions allow us to produce a new function by combining a function and an argument.
We can create a curry
function to return a function that has some arguments applied and let us apply the remaining arguments by writing the following:
const curry = (fn, ...args) => {
return (...moreArgs) => {
return fn.apply(null, [...args, ...moreArgs]);
};
}
In the code above, we have the curry
function, which takes a function fn
and some arguments args
after it.
Inside it, we return a function that returns fn
with all the arguments from both functions applied to it.
This way, we first apply the arguments in args
, then we apply the arguments in moreArgs
to fn
.
Now if we call it as follows:
const add = (a, b, c) => a + b + c;
const curried = curry(add, 1);
const result = curried(2, 3);
We first get a function with 1 applied to add
in curried
.
Then we get the final sum by applying 2 and 3 to curried
.
Therefore, result
is 6.
We can improve curry
by replacing apply
with the spread syntax:
const curry = (fn, ...args) => {
return (...moreArgs) => {
return fn(...[...args, ...moreArgs]);
};
}
This is cleaner and now fn
can be arrow functions in addition to a traditional function.
Memoization
We can do memorization by keeping previously computed results that we need later.
For instance, if we have a function that computes a Fibonacci number, we can write:
const fib = (num) => {
let a = 1,
b = 1;
for (let i = 1; i <= num; i++) {
[a, b] = [b, a + b];
}
return a;
}
In the code above, we store the values of a
and b
.
We keep the values of a
and b
so that we can use it to create new Fibonacci numbers with them.
Inheritance
JavaScript inheritance is done by using prototypical inheritance.
However, it has a syntax that resembles classical inheritance with the class syntax.
For instance, we can implement inheritance by writing the following code:
class Animal {
speak() {
//...
}
}
class Dog extends Animal {}
class Cat extends Animal {}
In the code above, we used the class
keyword to create constructors.
The extends
keyword is used to inherit members from Animal
in the Dog
and Cat
constructors.
Now if we create a new Dog
instance:
const dog = new Dog();
We have the __proto__
property in Dog
which is set to the Animal
constructor.
The speak
method is in the object that’s set as the value of the __proto__
property.
We can call the speak
method by writing dog.speak()
.
Prototypal Inheritance
If we have object literals, we can use the Object.create
method to inherit items from another object.
We can use it as follows:
const parent = {
foo() {
//...
}
}
const child = Object.create(parent);
console.log(child);
In the code above, we have the parent
object with the foo
method.
Then we can use the Object.create
method to create an object with parent
as the prototype.
If we log the child
object’s content, we have the __proto__
property which has the prototype of child
.
The __proto__
property has the foo
method.
Like inherited methods from class/constructor instances, we can call foo
as follows:
child.foo();
Conclusion
We can curry functions by returning a function that applies arguments to a function that hasn’t been applied yet.
Memoization can be done by storing values that will be used later so we can use them.
For inheritance, we can create constructors with the class syntax with the extends
keyword.
We can also use the Object.create
method for creating objects with our own prototypes.
This is a convenient way to create objects from a prototype object and inherit its properties.