What The Function?!

What The Function?!

Today I wanna talk about a couple concepts in JavaScript that I call the 'tricky stuff' mainly because these are the concepts that had me hurling expletives at my laptop then walking away in defeat šŸ˜”. First let me outline a few terms I may use in this post:

  • Execution Context

In JavaScript, the execution context refers to the environment in which the code is being executed. The two types we will be referencing in this post are the Global Execution Context & the Function Execution Context.

GEC is the default execution context where all code not contained in a function or object is executed while FEC is created when a function is invoked. The FEC has access to the code in the GEC however the inverse is not allowed in JavaScript. (Lexical Scoping)

Both execution contexts have a memory attached to it. Once the function has completed its execution and the FEC is closed, the local memory is deleted.

  • Call Stack

The callstack is JavaScriptā€™s way of keeping track of where it is in terms of the code execution. This term is likened to a stack of pancakes.

pancakes.gif

Iā€™ll start off by eating the chocolate chip one on top (chunk of code being executed). Once Iā€™m done (FEC is closed), Iā€™ll then start devouring the blueberry pancake thatā€™s next in line (execution moves back to the GEC or whatever execution context the chocolate chip pancake was on top of)

Higher Order Functions

Higher Order Function are functions that:

  • can be passed as input to other functions

  • can be returned as output from other functions.

I'd say these are functions that have finally realized their true potential.

let demoArray = [1, 2, 3, 4]

function doubleNum(num) {
    return num * 2
}

function cloneAndChange(array, instructions) {
    let newArray = [ ];
    for (let i = 0; i < array.length; i++) {
        newArray.push(instructions(array[i]))
    }
    return newArray
}

console.log(cloneAndChange(demoArray, doubleNum))

// returns [2, 4, 6, 8]

Here are a few of the most common higher order functions used in JavaScript:

  • Map
    The map method works by applying a function to each element of an array then returns a new array with the resulting values.
// array.map(callback function(current value, index, array)

The function map takes a function as its argument. That callback function takes some arguments too:

  • Current Value this is the element that the callback function is currently working on.

  • Index this is the index of the current value and is an optional argument.

  • Array this is the array that map was called upon and is also optional.

Lets see how map would work on demoArray:


let demoArray = [1, 2, 3, 4]
console.log(demoArray.map(doubleNum))

function doubleNum(num) {
    return num * 2
}

// returns [2, 4, 6, 8]

OR

let demoArray = [1, 2, 3, 4]
console.log(demoArray.map(x => x * 2))

// returns [2, 4, 6, 8]

A predefined function can be used or you could just define the function when you need it (parameter section)

  • Filter

The filter function works by creating a new array populated by elements that have passed the test provided as the callback function.

The method filter takes a function (that tests each element of the array using boolean logic) as its argument. If the element passes the test (evaluates to true), it will be copied into the new array.

let babyNames = ["Anne", "April", "Kylani", "Kimberly", "May", "Christina"]

let topPicks = babyNames.filter(name => name.length > 5)

console.log(topPicks)

// returns [ "Kylani", "Kimberly", "Christina"]
  • forEach

The forEach function executes a function on each element of an array once and usually returns undefined

let demoArray = [1, 2, 3, 4]
let m = demoArray.forEach(element => console.log(element * 2))

// returns 
2
4
6
8

Closures

For this explanation I'll be doing a walk through of blocks of code.

Let's start off with a simple function.


function demoFunction( ) {
    const mathEqn = 2 + 3
    return mathEqn;
}
const mathSum = demoFunction( );

When we run this block of code, the function definition labelled demoFunction as well as the constant mathSum, are stored in global memory. When that function is called, a function execution context is created and that is where the constant mathEqn is stored.

Once the equation has been evaluated, the result will be returned to the global execution context and stored in mathSum. The FEC is closed and its local memory is permanently deleted.

console.log(mathSum);

// returns 5

Lets say you have a function that returns a function or as we learned earlier a higher order function:


function demoFunction( ) {
    let count = 0

    function innerDemo( ) {
        count++
    }
    return innerDemo
}

const newInner = demoFunction( )

a few things happen when you run this code...

The first thing we'd do is create our GEC & place it on our callstack. We would then store the function definition known as demoFunction in our global memory after which we would declare a constant, newInner, but in order to get the value to be stored, we'd first have to run demoFunction.

/*
Our GEC

demoFunction = Function Definition

newInner = invocation of demoFunction
*/

This would cause the creation of a function execution context that would be placed on the top of the callstack. The variable count is stored in local memory and assigned the value 0. The function definition known as innerDemo is also stored in local memory.

/* 
Our FEC

count = 0
innerDemo = Function Definition
*/

The function would then return the function definition known as innerDemo, store it in the constant newInner and close the FEC. The FEC is removed from the callstack.

NB: Once the function's execution context closed, it took with it the local memory

Our constant, newInner would now look like this:


newInner = {
  count ++
}

This means that the label newInner can be run as a function.

But...

How would that work if the local memory containing the variable, count, was deleted? šŸ¤”

This is where Closures, come in. A closure, in essence, stores the function environment (any code that the function may make reference to or need to run) in a permanent memory that's linked to that function definition like a little knapsack on the back of the function. among-us-fanart-gifs-3.gif

And that's where we would find the count variable. When newInner is run once, count will be incremented to 1 and the new value for count will be stored in the knapsack. If I should run the function again, count would then be incremented to 2.

Thanks for reading! šŸ˜