The objective of this essay is to highlight the scope and use of advanced functions in javaScript. It is aimed at programmers who find it difficult to understand the concepts dealt with here. In this article, we are going to tackle advanced functions so that they can be really grasped and understood. The best way to do that is to practice them yourself then use the write up as a reference and as a guide. The topics covered are those that you will mostly see out in the wild.
SCOPE
Scope can be said to be the context in which values and expressions are visible and can be referenced. If a variable or other expression is not in the current scope then it is unavailable for use. Scopes can be layered in a hierachy so that child scopes have access to parent scope but not vice versa. Variables defined only within a function cannot be accessed from outside a function or within other functions.
Let us take a look at this function
function addTwenty(num) {
var add = 20;
return num + add;
}
console.log(add);
As you can see in the image above, after running this, it throws a reference error. This is because the
var add = 20;
is bound by the scope of the function. It is not visible outside the function, When we try to access it from outside the function. This called a function scope.
Another type of scope is the global scope Let us take a look at the function below
var globalVar = 'i am global';
function consult(str) {
return str + ' ' + globalVar;
}
console.log(consult('i am hungry'));
Here we are setting a variable called globalVar outside any other scope. Then inside our consult function, we are accessing this globalVar variable. Looking at the image above, we can see that we have access to the globalVar variable. Every other scope within this module has access to the globalVar variable. It is often best practice to avoid setting variables in this manner if possible. We want to avoid other parts of our code accessing and changing values that are used elsewhere which can lead to unexpected behaviour.
Another type of scope is the Block scope. Let us take a look at the sample code below
if (true) {
var x = 2;
}
console.log(x);
We have 3 methods of declaring a variable which is var, let and const. let and const are block scope while var is not. What this means is that variables declared using the let and const keywords are bound to the block they are defined within while var is not. That is why the code in the image above worked. Since var is not a block scope, it is not bound by the block scope and it is visible within the console.log();
Let us run the let and const keywords to see what we mean
if (true) {
let x = 2;
}
console.log(x);
When we try to access the above code it throws a reference error. This is because let and const are block scope and are not visible outside the block when we try to access them. They are variables that are evaluated during run time and bound by the block scope.
CLOSURES
An important javascript concept is CLOSURES. Closures can be thought of as when a function runs and it gets executed. The function is never going to execute again though it will remember that there are references to those variables. The child scope is always going to have access to the parent scope. When we run the code below
const first = () => {
const greet = 'Hi';
const second = () => {
alert(greet);
}
return second;
}
const newFunc = first();
newFunc();
we executed the
first();
function and assigned it to the
newFunc();
above. It is going to remember that there are references to those variables alive in the memory
first();
so that when calling the variable
newFunc();
the memory can have access to the parent scope. In reality, the code block executed will look like
const newFunc = Const second = () => {
alert(greet);
}
The function second is what we are really returning here. We can see that the variable
const greet = 'Hi';
is not within the scope of second here
const second = () => {
alert(greet);
}
What closure does is and it's a general rule in javascript is that the child scope which is
const second = () => {
alert(greet);
}
always has access to the parent scope. It's almost as if it remembers that there is references to those variables
alert(greet);
so that the web browser remembers that the child scope needs the variable greet.
Even though
first();
is never going to run again the web browser remembers the
const greet = 'Hi';
In essence closures can be defined as when a function runs and the function is executed, it is never going to execute again. The function will remember that there are references to those variables in memory so that the child scope has access to the parent scope.
Think of it this way, that children always have access to the parent scope but parents do not have access to the children.
CURRYING
The next we are going to talk about is CURRYING. Currying is the process of converting a function that takes multiple arguments into a
function that takes them one at a time. let us take an example function to see what i mean. We have a function
const multiply = (a, b) => a * b;
To Curry, we have to change the above function from a function that takes two parameters to a function that takes one parameter at a time
e.g
const curriedMultiply = (a) => (b) => a * b;
Think of the arrows above as functions. Let us run the function and see what I mean
const multiply = (a, b) => a * b;
const curriedMultiply = (a) => (b) => a * b;
curriedMultiply(3);
The above code returns a function that is
(b) => a * b;
which means running the code
const curriedMultiply = (a) => (b) => a * b;
where the number 3 will be assigned to
(a) =>
when we run the function, because it is a function inside a function it just returns
(b) => a * b;
To make it work, we can say
const multiply = (a, b) => a * b;
const curriedMultiply = (a) => (b) => a * b;
curriedMultiply(3)(4);
On running the above code we now get 12. Let me break it down. We created a function
const curriedMultiply
that accepts a parameter
(a) =>
and once the function is called it returns another function that accepts
(b) =>
and the function multiply
a * b ;
and the number 12 is gotten.
Now, why do we need to do this? We do this because it makes our code more extensible. For example, we can now do something like
const multiply = (a, b) => a * b;
const curriedMultiply = (a) => (b) => a * b;
const multiplyBy5 = curriedMultiply(5);
so that anytime we want to multiply a number by 5 we will have a function that was created that always multiply things by 5.
COMPOSE
Another important concept in javascript is COMPOSE. Compose is the act of putting two functions together to form a third function where
the output of one function is the input of the other. It is a really advanced concept in javascript which takes some time to grasp but
once it is understood a programmer can do anything. Let us give it a go
Example
const compose = (f, g) => (a) => f(g(a));
What is happening here? Looking at f and g, f is a function and g is a function because of the bracket. If we had a function that takes a num
const sum = (num) => num + 2;
What we can do with compose is we can
compose(sum)(sum)(5);
If the above code is run on a javascript engine we get 9 as the output. Let us take it one at a time
const compose = (f, g) => (a) => f(g(a));
const sum = (num) => num + 2;
compose(sum, sum)(5);
When we run the above function compose it returns
(a) => f(g(a));
When it runs it says
(a)=>
is 5. now the inner function
f(g(a));
is run. Here g is sum so we have the code basically saying
f(sum(5));
and sum from
const sum = (num) => num + 2;
means get me a num and add 2 giving us
f(2+(5));
which gives us 7, and now running the f function
sum(2+(5));
and also sum here is num + 2 we get the number 9.
Conclusion
That was a whole ton of stuff. I want you to remember the keywords we just covered because, in advanced javascript, functions are really really important. You will hear these words a lot and you will find tools and libraries that use this heavily. You donโt need to know the definitions, all you need is to read a piece of code and understand what is going on beneath the hood. By understanding the step by step process of how a function works, when you encounter them in the wild, you will understand how everything works.
SIDE EFFECTS AND FUNCTIONAL PURITY
When we create a function and we give it an input with or without a parameter, the function returns either a value or an undefined. Side effects are any action that happens inside the function that we donโt really know anything about. It reads, writes, or reacts to an external variable without our control. This is a side effect. Let us take a look at an example
var a = 2;
function b() {
a = 6;
}
A value is declared and assigned outside the function and within the function, it is assigned another value. It tends to create a side effect because the function will affect the outside world. Remember we want to think of functions as their own universe and if it starts affecting the outside world it affects the value we may get. So it is good practice to avoid these side effects and by avoiding these side effects we have what is called functional purity.
Functional purity is a concept where we say in order to write really good programs, we want to avoid side effects and always want to return a value. By avoiding side effects and returning a value, we create deterministic code. Whereby no matter what we input into a function, it will return a value that will be the same. It is a key principle in avoiding bugs.
Top comments (0)