Rethinking the foundations: An in-depth view of Javascript functions.

Rethinking the foundations: An in-depth view of Javascript functions.

First of all, the foundations.

You could say it's a paradox to be writing about functions in 2024, with the AI hype and all this intricate plethora of never ending new topics (beautiful word, isn't it?).

The thing is, I honestly believe most people, including myself, before diving deep into this topic, do not truly grasp the power of functions, we tend to overlook more "complex" aspects like scoping, closures (which might be one of the most powerful topics in JS), and I'm not even going to mention technical communication.

What's a parameter and what's an argument?

Well, the point of this article is not to give you a boilerplate of functions to use in React/Next or Vue.js. The goal is to give you clarity.

We'll start with basic implementations, then see how and why to generalize them, we'll explore scope, touch on technical communication, and peek into declarative programming. To wrap up, we'll refactor a function three ways and I'll point you to some challenges to solidify these concepts.

The Simplest of Beginnings

Take a look at these two functions:

function eightSquare(num) { return 8 * 8}
function nineSquare(num) { return 9 * 9}
 // keep going...        

what principle am I breaking here? ?? DRY (Don’t Repeat Yourself)

So What we do? We generalize the function but we leave a little bit of that function TBD: to be determined/ to be defined.

function squareNum(num) {return num * num}
squareNum(2) // 4
squareNum(4) // 16
squareNum(6) // 36        

Here's something really interesting: the more "to be defined" aspects you leave in a function, the more dynamically scoped it becomes.

While JavaScript is a lexically scoped language, meaning a variable's accessibility is determined by where the function is written, parameters add a twist.

By leaving these placeholders to be filled later, you're creating a bridge between lexical and dynamic scoping. It's as if you're saying:

"I'll decide what goes here when I use the function, not when I write it."
"This image illustrates lexical scoping in JavaScript. Notice how the inner function 'bar' has access to variables from its outer scope ('a' and 'b'), as well as its own parameter 'c'. This nested structure demonstrates how lexical scope works, with each inner function having access to the variables in its containing function."

This interplay between fixed structure and flexibility is what makes functions in JavaScript so powerful.

Each parameter you add is like pushing the boundaries of lexical scope one step further, opening new possibilities for how your code can behave.

function squareNum(num) { return num * num; }        

  • the parameter is allowing you to decide dynamically what calculation to do, but javaScript is a Lexical Scope language, did you think about that?
  • "num" is a placeholder, known as parameter, the provided value when running the function is an argument, which are not the same. It's a subtle distinction but refining our technical communication should be a permanent goal.


This behavior we just saw raises a natural question… what if we can abstract/generalize the functionality also?

What if I say, I want to decide the instructions later, and change them every time I use them?

That’s what Higher Order Functions are for.

From Repetition to Abstraction: Introducing Higher Order Functions.

Let's look at two similar functions:

function copyArrayAndDivideBy2(arr) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    newArr.push(arr[i] / 2);
  }
  return newArr;
}

function copyArrayAndMultiplyBy2(arr) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    newArr.push(arr[i] * 2);
  }
  return newArr;
}        

what principle am I breaking here? ?? DRY (Don’t Repeat Yourself)

So What we do? We generalize:

function copyArrayAndManipulate(arr, instructions) {
  let newArr = [];
  for (let i = 0; i < arr.length; i++) {
    newArr.push(instructions(arr[i]));
  }
  return newArr;
}        

But how is this possible?

In JavaScript, functions are first-class objects. They can be:

  • Stored in variables
  • Passed as arguments
  • Returned by other functions (This is when Closures happens, topic that I plan to tackle deeper on a next occasion.)

This is the key to Higher Order Functions (HOFs).

If you've been paying attention, you might realize that "copyArrayAndManipulate" is essentially the .map() method for arrays.

Now, what's the takeaway from this? There are more than one, but I would argue that seeing .map() under the hood will give you more clarity than you think. I also believe that now, you can probably code your own .reduce() method.

Below, I’ve shared a function called "copyArrayAndManipulateWithAccumulator", which mimics a simple .reduce().

Before checking it out, I challenge you to try coding your own version. I'm more than convinced that now you have all the necessary tools to write it, if you can't don't stress about it, you will see it's actually pretty simple.

Understanding that functions like .map() or .reduce() aren't magical but structured loops with callbacks puts you in control.

The next time someone praises these array methods, you’ll know what’s happening under the hood, and you'll have the ability to write your own versions.

This isn’t just about being a better coder; it’s about mastering your tools. So, next time you use .map(), .filter(), or .forEach(), you’ll truly understand what’s happening, and that's the difference between guessing and knowing.

If you’ve already coded your own reduce method, congratulations! Here's a super simplistic mimic of .reduce() for you to compare:

function copyArrayAndManipulateWithAccumulator(arr, callback, initialValue) {
  let accumulator = initialValue;
  for (let i = 0; i < arr.length; i++) {
    accumulator = callback(accumulator, arr[i]);
  }
  return accumulator;
}        

Lastly, JavaScript has been evolving as a more Declarative Language over the years. So, if you can solve a problem imperatively, force yourself to take it to the next level and try a declarative way solution, trust me, it will pay off.

Not only your understanding of the underlying logic will be stronger, your code will be:

  • More concise
  • More legible
  • Much easier to understand

Remember: Code is for humans. your code should be easily understood by other developers, actually that "other developer" I mentioned, he's you, couple of days after writing the code, so you'd better be clear when you declare your intent...

Adopt this motto and try to implement it: Declarative paradigm is about declaring your intent. WHAT do I want to do? I declare my intention and leave the language to handle the abstractions.

Let's set a last example before wrapping up:

// Imperative solution:

function sumAll(arr) {
  let total = 0;
  for (let i = 0; i < arr.length; i++) {
    total += arr[i];
  }
  return total;
}

// Declarative solution:

function sumAll(arr) {
  let total = 0;
  arr.forEach((num) => (total += num));
  return total;
}

// Declarative solution using reduce:

function sumAll(arr) {
  return arr.reduce((acc, curr) => acc + curr);
}        

Notice how each solution becomes more concise, more legible, and focuses more on WHAT we want to achieve rather than HOW to achieve it. That's the power of declarative programming and higher-order functions in action.

For those of you looking to challenge yourselves:

I've put together a repository with 10 problems to help you practice these concepts. As an example, I've solved Challenge 9 (one of my favorites) using three different approaches:

  1. The imperative way
  2. The declarative way
  3. Using .reduce()

Feel free to code and test your own solutions. You'll find all the instructions in this Repository.

This exercise isn't just about solving problems. It's about exploring different ways to approach them and deepening your understanding of JavaScript functions.



Bruno B.

Senior Frontend Engineer | Typescript | Next.js | React.js

2 周

So good brother ??, learned a lot.

Mukul Kumar

Ex-Insight Fullstack Developer | MERN, Next.js, Tailwind, Selenium | Creator of Tabber (500+ Downloads) | Open to New Opportunities

1 个月

Great insight about scopes and HOCs!

要查看或添加评论,请登录

社区洞察

其他会员也浏览了