DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Briggs Elsperger
Briggs Elsperger

Posted on • Originally published at briggs.dev

Understanding Callbacks

Understanding Callbacks

Callbacks seem to be a sticking point for people new to programming. Put simply, callbacks are functions that are passed into another function as an argument. With the many ways one can define a function in JavaScript, it's no wonder why callbacks get confusing.

Anatomy of a Function

JavaScript has many ways of defining a function, but they all follow a similar pattern and have the same pieces, they just look a bit different. There is more technical terminology surrounding functions, but we are going to gloss over them for now. (If you are interested, feel free to look up "Function Declarations" and "Function Expressions").

Normal Functions (Named Functions)

Normal functions, probably the first way you learned about creating functions. Understanding the anatomy of these will help you understand the other types of functions as well.

function funkyFunction(music, isWhiteBoy) {
  if (isWhiteBoy) {
    console.log('Play: ' +  music);
  }
}
Enter fullscreen mode Exit fullscreen mode

This is actually called a function declaration and is broken into a few parts.

  1. The function keyword
    • This tells the JavaScript compiler you are making a named function
  2. The name
    • This is the name of the function, and what you will use when you call it. It is also used in stack traces.
  3. The parameters
    • everything between ( and ) is a parameter, these must be separated by commas if there is more than one. There may also be nothing between the () if the function does not take any parameters. The parenthesis are required.
  4. The function body
    • This is where the function actually does something. This code gets run with whatever values are passed into the parameters.

Calling a function looks similar to declaring it. When you call the function, you type the name of the function and add () after. (without the function keyword and the body). Inside the () you may pass it the values you want the parameters you defined to represent. These arguments are used like variables inside the body of the function.

// Calling a function
funkyFunction('that funky music', true);

// This prints "Play: that funky music" in the terminal.
Enter fullscreen mode Exit fullscreen mode

Anonymous Functions

These are very similar to normal functions, with just a few differences. Anonymous functions are not 'named', and have a few different syntaxes. Even though they cannot have a name, they can be assigned to a variable. Even though when assigned to a variable they show up in stack traces, they are still considered an anonymous function. They may show up as 'anonymous function' in stack traces when passed into other functions as callbacks, however.

Anonymous functions are mostly used by passing them into other functions as a callback. This will become more clear later.

Each of the functions below are identical to the funkyFunction above in their 'funk-tionality'

// This example is still an anonymous function even though we used the `function` keyword, as it doesn't have a name.
const funkyFunction = function(music, isWhiteBoy) {
  if (isWhiteBoy) {
    console.log('Play: ' +  music);
  }
}

// This is called an arrow function, we'll get into these soon.
const funkyFunction = (music, isWhiteBoy) => {
  if (isWhiteBoy) {
    console.log('Play: ' +  music);
  }
}
Enter fullscreen mode Exit fullscreen mode

An anonymous function is just a function that does not have a name, this doesn't mean that it cannot be called. Each of the above functions can be called exactly the same way:

funkyFunction('that funky music', true);
Enter fullscreen mode Exit fullscreen mode

And this is because functions are 'first class citizens' in JavaScript and can be assigned to variables. Or passed as an argument to another function.

Arrow Functions

These are just a shorter way to write a function. They do have some special rules however, and understanding the rules imposed by arrow functions will help you understand callbacks. We're going to ignore the this binding rules for these functions for now.

  • If there is only one argument, the parenthesis () can be omitted
  • if arrow functions are one line, the brackets {} can be omitted.
    • When omitting the brackets, the arrow function returns the evaluated expression without requiring the return keyword.

The functions below are variations of the rules above

const playThe = (funky) => {
  return funky + " music";
}

const playThe = funky => {
  return funky + " music";
}

const playThe = funky => funky + " music";

// You can call all of these functions like: `playThe('blues')`
Enter fullscreen mode Exit fullscreen mode

Below are some examples of an arrow function without an argument. These functions are all identical as well. Notice the () in place of any named arguments. It is required because there aren't any parameters.

const playThat = () => "funky music";

const playThat = () => { return "funky music"; }

const playThat = () => {
  return "funky music";
}
Enter fullscreen mode Exit fullscreen mode

Key Point

Take some time and study the function examples above and note how they are similar and how the same parts exist in both, with the exception of the function keyword.

What Callbacks Look Like

You most likely have seen, or even used, callbacks and not realized it. They are used frequently in JavaScript. Understanding JavaScript is impossible without understanding callbacks. Below is an example of something you may have run into before.

const notes = ['do', 're', 'me'];

notes.forEach((note) => console.log(note));
Enter fullscreen mode Exit fullscreen mode

This is the forEach array method. This method simply takes a callback function as its argument. (Don't forget that forEach is a function itself).

There are many other ways to do the same thing (as is tradition in JavaScript), below are a few more ways to write this code:

const notes = ['do', 'ray', 'me'];

notes.forEach((note) => { 
  console.log(note);
});

notes.forEach(function(note) {
  console.log(note); 
});

// This one is tricky, but will make more sense later
notes.forEach(console.log); 
Enter fullscreen mode Exit fullscreen mode

How Callbacks Work

To state it once more: Callbacks are just functions passed into other functions as arguments (as a parameter).

Iterator Functions

Below is what forEach might look like under the hood, notice it calls the callback function each time it loops over an item.

function myForEach(array, callback) {
  for (let i = 0; i < array.length; i++) {
    callback(array[i]); // This is when the callback function gets called, or executed
  }
}

// You would call it like this:
const myArry = [2, 3, 4, 2];
myForEach(myArry, (item) => {
  console.log(item + 2); 
})
Enter fullscreen mode Exit fullscreen mode

WHOA, hold up. Where did item come from?

This came from the function myForEach calling the callback with an argument. The line with callback(array[i]) is calling the callback function with an argument, which we defined inline as an anonymous function. Below are more examples of how this could be called.

const myArry = [2, 3, 4, 2];

// We do not need the `()` in this case, as we only have one argument and we are using an arrow function
myForEach(myArry, item => console.log(item + 2)); 

// We can pass arguments to this kind of anonymous function as well
myForEach(myArry, function(item) {  
  console.log(item + 2) 
});

// This time we are declaring the function we want to use as a callback
// Notice we define `item` as a parameter to be passed in when it's called by the `myForEach` function.
function printItemPlusTwo(item) {
  console.log(item + 2);
}

// `item` is passed into the function, we do not need to declare it here because we declared it elsewhere. 
// It is the same as the 'console.log' example above except we declared our own function.
myForEach(myArry, printItemPlusTwo); 
Enter fullscreen mode Exit fullscreen mode

Another good example of how callbacks work might be the .map method (read more on MDN), below is one way it might be implemented.

function myMap(array, callback) {
  const myNewArray = [];

  for (let i = 0; i < array.length; i++) {
    const callbackResult = callback(array[i]);
    myNewArray.push(callbackResult); 
  }

  return myNewArray;
}


// This could be called like this:
const addedArray = myMap([1, 2, 3], (arrayNum) => {
  return arrayNum + 2; 
});


// OR
const addedArray = myMap([1, 2, 3], (arrayNum) => arrayNum + 2)
Enter fullscreen mode Exit fullscreen mode

Event Listeners (DOM)

Event listeners in JavaScript seem to be confusing to people, but after understanding callbacks, these should be a lot easier to understand.

Let's review what they look like, see if you can pick out the different things going on.

const element = document.querySelector("#myId");
element.addEventListener('click', (event) => {
  console.log(event.target.value);
  // `event` is passed into the callback from the `.addEventListener` function when it receives a 'click' event.
});
Enter fullscreen mode Exit fullscreen mode

If you notice, the second argument (value you pass into a function) to addEventListener is a function. In this case it's an anonymous arrow function. This piece of code could have also have been written like this and it would behave identically.

const element = document.querySelector("#myId");
element.addEventListener('click', function(event) {
  console.log(event.target.value);
});
Enter fullscreen mode Exit fullscreen mode

Part of what confuses people is the event object. Where does it come from? How does it get there?

This event object is passed into the callback function by the .addEventListener function. A function is calling another function.

This is because.... Callbacks are just functions passed into another function as arguments.

That means we can declare a function outside of the argument list and just add it by its name as well. Like so:

function myEventHandler(event) {
  // do something, probably with 'event'
}

const element = document.querySelector("#myId");
element.addEventListener('click', myEventHandler);
Enter fullscreen mode Exit fullscreen mode

Notice how we didn't 'call' the function called myEventHandler? If we were to call it inside the parameter list, the function we called myEventHandler would run immediately and give the addEventListener the result of calling that function. (in this case, it would be undefined)

Conclusion

Callbacks are an important part of JavaScript, they are vital to understand, even with the onset of promises and async/await. Callbacks get called by another function, so you don't have to call them in the arguments, ( Calling a function is using a function's name and adding () to the end of it, like console.log() )

These are something that you will learn if you give yourself time, understanding how they work will make your JavaScript career a lot easier!

Top comments (20)

Collapse
peerreynders profile image
peerreynders • Edited on

These are just a shorter way to write a function.

I understand this is content for beginners ... but there seems to be this idea that arrow functions were introduced in ES2015 for their fashionable terseness.

The reason for their inclusion is that they are different to traditional function expressions - which is related to their use as callbacks.

MDN: "Arrow functions establish this based on the scope the Arrow function is defined within."

See also: Function Context.

In practical terms this means that when an arrow function is created inside an object method it automatically binds to the this of the object the method was called on, so it retains access to the original creating object even when it's passed as a callback to another function (or method of another object).

Regular functions do not behave this way. Regular functions have to be explicitly bound to the object.

As a trade off arrow functions don't support apply, bind, or call and as a consequence arrow functions cannot be shared via the prototypal inheritance mechanism (given that each arrow function's this is statically bound it no longer can be shared via a dynamically passed this).

function log(text) {
  console.log(`${text}: ${this.someValue}`);
}

const instance = {
  someValue: 'Instance',
  passArrow() {
    const callback = (text) => log.call(this, text);

    setTimeout(callback, 100, 'Arrow');
  },
  passFn() {
    const callback = function (text) {
      log.call(this, text);
    };
    setTimeout(callback, 200, 'Function');
  },
};

globalThis.someValue = 'Global';
instance.passArrow(); // Arrow: Instance
instance.passFn(); // Function: Global
Enter fullscreen mode Exit fullscreen mode
Collapse
i3uckwheat profile image
Briggs Elsperger Author

That's exactly why I added We're going to ignore the this binding rules for these functions for now. in the article, perhaps I should have expanded on that a bit more. This comment here explains it very well though!

Collapse
peerreynders profile image
peerreynders

Re-reading that paragraph again:

"Arrow functions differ from regular functions in some nuanced ways. To simplify the discussion we're steering clear of those differences."

Beginners tend to latch onto "just a shorter way to write a function" as "the truth" because it's so (convenient and) easy to remember - using it as a justification to use arrow functions exclusively even when it's inappropriate (e.g. as class methods).

Thread Thread
i3uckwheat profile image
Briggs Elsperger Author

I'll consider making some edits, thank you

Collapse
alexerdei73 profile image
AlexErdei73

Thank Mr Elsperger for the great article. The article is really illuminating regarding callbacks. He is well aware that there is a minor difference regarding the context between arrow functions and regular functions. It is perhaps more important than the syntax difference as anybody with a little ES6 JS experience can refactor one form to the other. This comment is great to point out the real difference between the two with this great code sample. Maybe the code sample can be simplified slightly to make it more obvious, what's going on.
The important question is weather the article should contain this difference as detailed as in this comment. In my opinion Mr Elsperger is not far from the truth that real beginners can get confused with details like this. I think he has got experience with these people as he contributes actively to The Odin Project, which is a curriculum for people, who want to become self trained web developers, like myself.
As far as I have seen this is the only real difference between arrow functions and regular function expressions, therefore it would be great to include it in the article in some less confusing form. He could use for example small print and warn beginners that they can skip this part not to get confused. This is common practice in scientific articles.
Why is this fine detail less important? JS is not a language for primarily doing OOP, but functional programming. It means listeners hardly ever use 'this'. It's a concept of OOP, where inheritance is highlighted. Of course people, who want use JS for OOP or people who use React class components (OOP syntax) must be aware of all this.

Collapse
peerreynders profile image
peerreynders

JS is not a language for primarily doing OOP, but functional programming.

  1. JavaScript is not a "functional programming language"
  2. In JavaScript class is merely a template for creating objects; it's not a class in the class-based object oriented sense.
  3. this is called the function context; so it can be used to access the object the function was invoked on but it is also used in other ways.

Please read this: Function-Oriented

Thread Thread
alexerdei73 profile image
AlexErdei73

You are of course right. I don't have very strong language theoretical knowledge. I am only fluent in two languages.

  1. Object Pascal - I am fluent, but my level is maximum low intermediate. I didn't have a chance to do very sophisticated OOP with this, when I was young, I did basic OOP. It's a strongly typed compiled language, very fast after compilation and had an editor with amazing features that time. By today it's nothing of course. The damned thing is still very fast though.
  2. Java Script - I think I am better with this, thanks to The Odin Project and Mr Elsperger and a lots of other guys in their community. My level might be high intermediate in this. Where I happened to use Java Script in practice on the front-end I did some OOP with it. OOP is possible with a couple of different syntax in JS. Classes are practical, and these are similar to other languages, like Object Pascal for example. Despite of this it's far from ideal to do OOP in it. Some important syntax elements are missing (like excess modifiers) and auto completion, which can be a great practical help, is restricted here, because the weakly typed nature of the language. ES6 classes are here just a different syntax for the Constructor function syntax, which is the way how the language is built itself. So yes the language is built on OOP principles itself, but the inner working is different from typical OOP languages and the OOP tools are restricted. In my opinion JS is not really for OOP programming for the above mentioned reasons. Those, who want to do OOP with it, are better to use TypeScript. TypeScript comes with another syntactic layer on the top of JS, so it's the tool for OOP programming. What we usually do with JS only is not really OOP. JS is much more often used functional programming ways and functions are the most important building blocks here. This is certainly true for programming in modern React, where function components are the building blocks of the UI. They are more natural there than the class components, although the two might be equivalent apart from the syntax. Your code sample for the differences between arrow functions and regular function expressions is a great and actually the only example, which I have seen. This is why I liked your comment. The only other place, where I have seen the same things explained on the right way is an older React tutorial. It used 'this' a lot, because that tutorial only operated with class components. The difference was significant there in some cases and the tutorial pointed it out well. Although you are perfectly right, from a pedagogical point of view, Mr Elsperger's approach might be better for beginners, as the difference hardly ever shows in practical JS. On the other hand for the sake of completeness, he could include your sample and explanation in this article, but only for people, who are not truly beginners. So it needs to be well separated from the main content.
Thread Thread
peerreynders profile image
peerreynders • Edited on

I think you are mistaking my position that it is important to understand this in reference to functions as some kind endorsement of OOP in JavaScript. Nothing could be further from the truth (as should be evident in this comment).

My contention is that understanding the relevance of this is an important part of building JavaScript competenceβ€”completely independent of any OOD/OOP competence. In fact MDN calls it class context in the context of class constructors.

When you look at the documentation under this it's also called the execution context.

this is the execution environment that is instantiated by the JavaScript runtime before the function was invoked. Granted 99.9% of the time this is done to emulate a method call on an object but it has other uses.

Here is a weird one:

(function (b,c) {
    return this + b + c;
}).apply(4, [5, 6]);  

// 15
Enter fullscreen mode Exit fullscreen mode

JavaScript is still first and foremost an imperative language. Please see this comment and this one. For example, JavaScript isn't immutable nor does it support persistent data structures and while recursion is supported, tail call optimization (TCO) isn't standard so there is always the risk of blowing the stack.

The fact that JavaScript can support a functional style is largely due to the fact that it was initially developed as Scheme in the browser.

From a beginner's perspective I think a case could be made that arrow functions should be avoided until such time that they are fully versed and comfortable in using standard function declarations and function expressions given that those are the fundamental building blocks of the language; arrow function expressions are a mere convenience feature.

In fact prior to their introduction in ES2015 there were concerns expressed to the TC39 that developers would over-adopt arrow functions; these concerns seem to be bearing out, as now it isn't unusual for beginners to be misapplying them.

Thread Thread
alexerdei73 profile image
AlexErdei73

I think we perfectly understand each other, although we form our opinions on a very different basis. You seem to have a high language theoretical knowledge and the knowledge of the history of more programming languages. I only have some very practical programming knowledge mainly only in two languages. Actually the functional programming paradigm or the elements of it is only known for me by JS and completely new, as it is just missing from Pascal. That's truly imperative language without any functional programming tools apart from functions of course.
It is clear for both of us that JS is based on imperative programming and OOP, so it is not a truly functional programming language. It was clearly developed on OOP principles.
All I tried to say is when we apply JS in practice, especially on beginner level, we mainly use functions and functional programming ways of solving our practical problems, instead of OOP. So we hardly ever get bogged into problems with function context, simply because we just do not use too often the value of 'this' inside our functions.
We also agree that using arrow functions too early is itself can be confusing. In my opinion it is better to rely on constructs, which are similar and exist in other languages too. These are usually more elementary constructs. So for me it is obvious to start with regular functions, because they almost just like functions in good old Pascal. Arrow functions are a new concept compared to the good old Pascal. Where is the point, when we can't avoid using them in JS at least without applying 'noisy', not beginner friendly syntax?
My answer would be that when we are doing OOP or even using React class components we cannot avoid using 'this' inside our functions in some cases. The reason is that we need to reference to the object instances, which contain the function. We expect the value of 'this' to be bound to these object instances, because it is the case in good old Pascal and that's why we use 'this'.
In most cases JS works just like good old Pascal, regarding the value of 'this'. In certain cases unfortunately JS fails to behave like we expect. These are not very common cases, instead some marginal situations, like in your code sample. Your instance is not an instance of a class and the functions inside are not class methods either. In that case JS would behave just like good old Pascal, and everything would be fine. In your sample, on the other hand, the two functions are in a simple object and in cases like this, 'this' is only bound to the object automatically if you use an arrow function. If you use a regular function 'this' is bound to the global object or undefined. This JS behaviour is pretty unnatural and not like the good old Pascal at all. And this is the real reason, why arrow functions are unavoidable in these marginal cases, unless you use some 'noisy' explicit binding. Arrow functions of course are used much more often, just because these are simple and short at least after you got used to them.
I get to the same conclusion again, you are absolutely right, but to overwhelm a beginner with this is not wise. Mr Elsperger could edit his article a bit to explain how arrow functions are different from regular functions with binding the value of 'this', but he should separate it from the main material some way. Beginners will hardly ever meet the problem of function context in practical JS until they start using 'this' in their functions. At that point they are doing OOP in JS or programming with React class components, so they are not truly beginners any more.

Thread Thread
peerreynders profile image
peerreynders

We expect the value of 'this' to be bound to these object instances

…unfortunately JS fails to behave like we expect.

This JS behaviour is pretty unnatural …

The issue is that you are falling prey to your own expectations.

JavaScript is most despised because it isn’t some other language. If you are good in some other language and you have to program in an environment that only supports JavaScript, then you are forced to use JavaScript, and that is annoying. Most people in that situation don’t even bother to learn JavaScript first, and then they are surprised when JavaScript turns out to have significant differences from the some other language they would rather be using, and that those differences matter.

[Douglas Crockford, JavaScript - The Good Parts, 2008; p.2]

Do yourself a favour and do not expect JavaScript to behave like any other language. It's its own thing.

Don't let superficial familiarity get the better of you. Familiarity is a trap that is going to cost you considerable time and frustration.

Thread Thread
alexerdei73 profile image
AlexErdei73

This is certainly true, that the behavior of 'this' is not the same in JS as it is in the good old Pascal and perhaps other OOP languages. I think too much theory leads us too far. Let me share with you the video tutorial, where it is explained when it's happening with React class components.

The problem arises at 01:01:11 and it is explained pretty well. I also prepared a code sample according to your code and his React example. This is the same problem without React, and the code is more elementary than yours and a bit more practical, but also longer.

Image description

And the html:

Image description

It's not for truly beginners, but it is understandable for those, who finished the OOP parts of The Odin Project. I think it's natural to show it in OOP, because we use 'this' there more often.

Thread Thread
peerreynders profile image
peerreynders

Please use code blocks in the future for code. Images of code without a suitable alternate text description are not considered accessible.


The problem arises at 01:01:11 and it is explained pretty well.

I'm well aware of this issue and it identifies the primary use case of why arrow functions were introduced with ES2015 before it became fashionable to use arrow functions exclusively. It doesn't change that arrow functions were introduced as a special case feature, not a replacement for the original function syntax which is how many people use them.

I think it's natural to show it in OOP, because we use 'this' there more often.

Again you are judging this by your previous non-JS experience without fully investigating what this actually is in JavaScript. While that is a natural thing to do, all technology is artificial and therefore is shaped by the context in which it is invented. The circumstances around the development of Object Pascal were very different to the circumstances of the development of JavaScriptβ€”and it shows.

Wishing one was more like the other is a natural thing to do but ultimately is only wishful thinking destined to be disappointed.

If I had to guess this was likely added to JavaScript so that functions and objects can be easily composed. Note that I'm saying "objects", not "classes". In JavaScript you can actually "code" with objects; in most OOPLs you cannotβ€”you are relegated to defining classes that can create the objects for you at runtime. Clearly this and the prototype chain inspired by Self made it possible to emulate OOP in JavaScript but at its core this is just the function's execution context (a special, invisible argument) set by the JavaScript runtime.

Under 10.3 Execution Contexts

An execution context contains whatever state is necessary to track the execution progress of its associated code.

That's it.
Nothing about objects or OOP.
In JavaScript this can be a primitive valueβ€”it doesn't even have to be an object.

And as we are talking about event handling I'd like to direct you to DOM handleEvent: a cross-platform standard since year 2000 (And the workaround to use it in React; vanilla demo, Preact demo).

By passing an object with a handleEvent() method, the this binding problem inherent to functions as event handlers goes away; React implemented it's own synthetic event system that never allowed for that possibility which is why the workaround is necessary.

The point is that this often gets involved when functions and objects work together; the appearance of this doesn't automatically imply the practice of class-based object-orientation in JavaScript.

Thread Thread
alexerdei73 profile image
AlexErdei73 • Edited on

Thank you for explaining your opinion with such a detail about arrow functions in JS. I apologize for only giving screenshots of my code sample. It might be worth to play around it a bit. I created a tiny repository, you can clone to try the code, arrow-function-example. Because this example does not contain React, other solutions are possible, as you pointed out, apart from an arrow function or binding the value of 'this'. You can for example add a field, called parentComponent, to the buttonFunction object. You can use that in the constructor to add the value of 'this' at that point to the button. In the listener you can reach that with 'this.parentComponent' and the value will be the instance object. Even in React the situation has been changed by introducing function components and hooks. So the video shows the technology at around 2015, when arrow functions got into the language.
Your valuable and rich opinion helped me to change a couple of misunderstanding or misinterpretation, which I had about this question. You pointed out to me the following things:

  1. My opinion is strongly biased by my previous experience in Object Pascal.

  2. The analogy between JS and Object Pascal (and any other language) can be helpful but comes with limitations, simply because these technologies were developed in different times with different purposes, therefore can be and are different.

  3. Particularly the role of 'this' are different in these two languages. In Object Pascal 'this' means strictly the object instance, which the class generates. In JS the meaning is more general and it just simply means the running environment, which a function is getting executed on. This can be even undefined or the value can be just a number or string, not necessarily an object and can explicitly bound to the function with the bind method, not like in Pascal.

  4. In JS the function and Object are the more basic terms and the code can be fully built up from these not like in Pascal. OOP is not possible without classes in Pascal and we all know that classes just a syntactic convenience in JS. I have already known this but it is refreshing to see it from your point of view too.

  5. Arrow functions got into the language to explicitly bind the value of 'this' without applying the bind method. This can solve some marginal problems, but you are not an advocate to apply them in unjustified cases, just because their syntax seem to be simpler or more fashionable than regular functions. So regular functions are the ones which we should generally apply apart from the marginal cases, what arrow functions actually solve. I agree with this. Even if you check my code sample you can see that the arrow function as a method becomes an instance method, but the regular function is a prototype method. So arrow functions are not recommended as methods on the favor of regular functions generally.

  6. I still think this topic is strictly not for beginners, although Mr Elsperger could have included some more detailed explanation instead of simply avoiding it. This explanation should be based on some simple code sample for interested learners, but also should be separated from the main text. It could be beneficial for interested learners, because it helps us understand better the inner workings of the language.

Thank you again for sharing with me your view about this question.

Thread Thread
peerreynders profile image
peerreynders • Edited on

So regular functions are the ones which we should generally apply apart from the marginal cases, what arrow functions actually solve.

You are phrasing this as an absolute rule.

My context is "when teaching beginners".

Ultimately a senior developer is going to do whatever they want but they are supposed to understand the difference and when they predominantly use arrow functions because that's "all they need" it will hopefully be in code that won't be reviewed by beginners to learn the ins an outs of JavaScript.

In my experience when beginners are introduced to arrow functions too early they make mistakes like using them in class definitions (think organizing related functions around a frequently used data structureβ€”not necessarily OOP) when the shorthand syntax is more appropriate. They simply don't realize that the arrow functions are (unnecessarily) recreated on every object when a single function on the prototype chain will work for all the objects (n.b. my personal preference is to organize functions within modules).

I went through an "avoid this" phase myself (OOP with Functions, How to decide between classes v. closures in JavaScript) but I think it's impossible to work with JavaScript and avoid open source software entirely. And working with OSS you will invariably have to read other developer's code who use a coding style that you have absolutely no control over; at that point you better know how JavaScript works and that includes this.

The other thing you are missing out on with arrow functions is hoisting. While variable hoisting can be confusing, I find function hoisting incredibly useful. It lets me write a "unit of work", give it a DAMP (descriptive and meaningful phrase) name and then put the function anywhere in the source order where it is out of the way because at that point in time the DAMP name tells me everything I need to know.

Also arrow functions can't be named. These days some JavaScript runtimes will grab the name of the variable they are assigned to and add it to the debugging meta data but there has been a long-standing recommendation to use named function expressions to make it easier to identify what you are looking at in the debugger (example).

we all know that classes just a syntactic convenience in JS.

Again careful with absolutes.

A JavaScript runtime can expose native functionality via a class-like interface which is actually a class in every sense of the word (JS classes are not β€œjust syntactic sugar”). The best example is custom elements which can cause problems when you need to compile down to ES 5.1.

Thread Thread
alexerdei73 profile image
AlexErdei73

I am of course well aware the function hoisting and I use it just like you do in every day development. When we compare arrow functions and regular ones, arrow functions can be compared better with regular function expressions. These behave the same regarding the hoisting.
The question of variable hoisting is something new for me as I always declare and even try to initialize variables before I use them. This obviously comes from the root of my Pascal training. I would have never thought of the possibility that it can be done any other way even in JS. I may be very wrong with it though:) This opportunity does not mean I will give up my habit regarding variables in my coding style.
The wide range of knowledge you have regarding very different programming languages and about their history and the theory behind them is amazing and unique. You remind me someone, I had conversation with before, but you might be the exact same person just on a different platform. It would be a great pleasure if you could register on my website and post the talk about simplicity there as well. I liked this talk because it underlines the importance of reasonability in your code quality. As he talks about the growing elephant and your task as drag it when it is really big. For him simplicity means the knowledge in design, which keeps this task reasonable. I really liked this talk.
The link is Fakebook. It is just a personal project with a few friends and strangers on it, but that would be a great thing to welcome you there, if you have the time for it.

Collapse
marcomoscatelli profile image
Marco Moscatelli

Great content for beginners!

Collapse
konadulord profile image
Konadulord

Head aching but I will grab it

Collapse
darkwiiplayer profile image
π’Š©Wii πŸ’–πŸ’›πŸ’šπŸ’™πŸ’œπŸ’πŸ’Ÿ

Very nicely explained πŸ˜€

Collapse
brunoj profile image
Bruno

love this

Collapse
khaledyd profile image
khaledyd

it's what i needed the most to understand the callbacks and fucntion , becouse of this post i can understand what's funtion and whats not this is amazing , thank you so much

🌚 Life is too short to browse without dark mode