Exploring JavaScript: Higher Order Functions, Callbacks, Closures, the Event Loop and Classes

Recently, I went through the FrontendMasters course, "JavaScript the Hard Parts" by Will Sentance. If the title of this post sounds like a lot, that’s because it is. That said, the course was clear and engaging. I would highly recommend it. Some of what I learned is described below: Parameters vs. Arguments: The distinction between a parameter and an argument seems small, but I like the idea of being able to think and speak precisely. Part of that requires using words correctly even when I think they are interchangeable. A parameter refers to the variable name that's used as a placeholder when a function is defined. An argument is the actual value that is passed to a function when that function is called. function logString(str){ console.log(str); } logString("happy"); In the above example, str is the parameter and "happy" is the argument. Higher Order Functions vs Callbacks: In JavaScript a higher order function is a function that accepts or returns another function. A callback is a function that is passed into another function as a parameter. function logString(str){ console.log(str); } setTimeout(logString, 1000) In the example above, logString is the callback and setTimeout is the higher order function. I gained a better understanding of how JavaScript executes lines of code, including creating new execution contexts when a function is called. I reviewed concepts about the call stack and stack traces. I learned more about closures which I thought I understood. I didn’t have an appreciation for how powerful they can be. What stuck with me was the idea that when a function is created and returned inside of another function it has access to relevant data that existed around it at the time of it's creation. Essentially, (if I anthropomorphize a closure), it has memory that it carries with it. function outer(){ let count = 0 function incrementCounter(){ counter++; } return incrementCounter } const myNewFunction = outer(); myNewFunction() // counter = 1 myNewFunction() // counter = 2 In the example above, myNewFunction can continue to act on the variable counter even though the function outer is no longer on the call stack and JavaScript has left outer's execution context by the time myNewFunction is called. Prior to this, I just thought, "a closure if a function that returns another function". Which is true, but it is so much more than that too.. The Event Loop I didn't know that I didn't know what the event loop was. In order to understand the event loop, I had to learn about the boundary between JavaScript and the Web Browser. I learned that when I call a function like setTimeout it's actually the browser that is setting and keeping track of the timer and that as the rest of my code executes, the callback I passed to setTimeout will (at some point depending on the time I set) be placed in a task queue. The event loop will figure out when the call stack is empty and all of the synchronous global JavaScript code has completed and then move the callback from the task queue to the call stack to be executed. And this callback will always run after the synchronous code before it, no matter how long it takes. Even with something like setTimeout(callback, 0). This is of course, assuming that there is nothing on the micro task queue as a result of calling some asynchronous code using Promises because the functions in the micro task queue receive higher priority than the functions in the task queue Classes and the new keyword I've used JavaScript classes before, but I liked that this course worked up to them by demonstrating different scenarios in which you might want to have your data and the functions that act on that data grouped together. Then, the different ways that objects can be created in JavaScript, and finally the syntactic sugar that classes provided to do it in a way that is readable. Although Will makes the case that this approach is deceptive in that, many people think they understand what is going on under the hood, but don't. I learned that the new keyword used for creating an instance of an object: Automatically creates an object assigned to this in the execution context of the “creator” function.. Links, via the hidden __proto__ property, the automatically created object to its fundamental object's [[prototype]] key, and the value assigned to that [[prototype]] key is itself an object, that holds data and methods. This gives the automatically created new object access to methods it does not have explicitly defined on itself, taking advantage of JavaScript's prototypal chain. Returns this automatically created and linked object from the creator function. And the class keyword gives us the chance to define this creator function (function-object) with all of the data and methods that we want it to have in one place, but doesn't really change anything under the hood. It does look nice though. The End All of this

Feb 6, 2025 - 19:27
 0
Exploring JavaScript: Higher Order Functions, Callbacks, Closures, the Event Loop and Classes

Recently, I went through the FrontendMasters course, "JavaScript the Hard Parts" by Will Sentance. If the title of this post sounds like a lot, that’s because it is. That said, the course was clear and engaging. I would highly recommend it.

Some of what I learned is described below:

Parameters vs. Arguments:

The distinction between a parameter and an argument seems small, but I like the idea of being able to think and speak precisely. Part of that requires using words correctly even when I think they are interchangeable.

A parameter refers to the variable name that's used as a placeholder when a function is defined. An argument is the actual value that is passed to a function when that function is called.

function logString(str){
  console.log(str);
}

logString("happy");

In the above example, str is the parameter and "happy" is the argument.

Higher Order Functions vs Callbacks:

In JavaScript a higher order function is a function that accepts or returns another function. A callback is a function that is passed into another function as a parameter.

function logString(str){
  console.log(str);
}

setTimeout(logString, 1000)

In the example above, logString is the callback and setTimeout is the higher order function.

I gained a better understanding of how JavaScript executes lines of code, including creating new execution contexts when a function is called.

I reviewed concepts about the call stack and stack traces.

I learned more about closures which I thought I understood. I didn’t have an appreciation for how powerful they can be. What stuck with me was the idea that when a function is created and returned inside of another function it has access to relevant data that existed around it at the time of it's creation. Essentially, (if I anthropomorphize a closure), it has memory that it carries with it.

function outer(){
  let count  = 0 
  function incrementCounter(){
   counter++; 
  }
  return incrementCounter
}
const myNewFunction = outer(); 
myNewFunction() // counter = 1 
myNewFunction() // counter = 2 

In the example above, myNewFunction can continue to act on the variable counter even though the function outer is no longer on the call stack and JavaScript has left outer's execution context by the time myNewFunction is called. Prior to this, I just thought, "a closure if a function that returns another function". Which is true, but it is so much more than that too..

The Event Loop

I didn't know that I didn't know what the event loop was. In order to understand the event loop, I had to learn about the boundary between JavaScript and the Web Browser. I learned that when I call a function like setTimeout it's actually the browser that is setting and keeping track of the timer and that as the rest of my code executes, the callback I passed to setTimeout will (at some point depending on the time I set) be placed in a task queue. The event loop will figure out when the call stack is empty and all of the synchronous global JavaScript code has completed and then move the callback from the task queue to the call stack to be executed.

And this callback will always run after the synchronous code before it, no matter how long it takes. Even with something like setTimeout(callback, 0). This is of course, assuming that there is nothing on the micro task queue as a result of calling some asynchronous code using Promises because the functions in the micro task queue receive higher priority than the functions in the task queue

Classes and the new keyword

I've used JavaScript classes before, but I liked that this course worked up to them by demonstrating different scenarios in which you might want to have your data and the functions that act on that data grouped together. Then, the different ways that objects can be created in JavaScript, and finally the syntactic sugar that classes provided to do it in a way that is readable. Although Will makes the case that this approach is deceptive in that, many people think they understand what is going on under the hood, but don't.

I learned that the new keyword used for creating an instance of an object:

  1. Automatically creates an object assigned to this in the execution context of the “creator” function..
  2. Links, via the hidden __proto__ property, the automatically created object to its fundamental object's [[prototype]] key, and the value assigned to that [[prototype]] key is itself an object, that holds data and methods. This gives the automatically created new object access to methods it does not have explicitly defined on itself, taking advantage of JavaScript's prototypal chain.
  3. Returns this automatically created and linked object from the creator function.

And the class keyword gives us the chance to define this creator function (function-object) with all of the data and methods that we want it to have in one place, but doesn't really change anything under the hood. It does look nice though.

The End
All of this made me feel like I have a better understanding of JavaScript than I did before. I believe my mental model of the language is a little clearer. I am left with some questions though:

  • How can I apply this new understanding to write better code?
  • What ways can I use this information in the wild?
  • I've seen people say that it is important to understand JavaScript classes, but not to use them. Why? What are the trade offs?