Complete guide to arrow and this functions in JavaScript

  • Arrow functions provide a concise syntax and a lexicon that inherits the context in which they are defined, greatly simplifying callbacks and asynchronous code.
  • Traditional functions maintain a dynamic controllable `this` with `call`, `apply`, and `bind`, and are suitable for constructors, object methods, and classic OOP patterns.
  • Properly combining arrow functions with ES6 (let, const, destructuring, spread, and array methods) is key to writing modern, immutable, and expressive code in JavaScript and React.
  • Knowing when to avoid arrow functions (for example, as object methods or constructors) is just as important as knowing how to take advantage of them in modules, event handlers, and functional logic.

arrow and this functions in JavaScript

If you've been programming in JavaScript for a while, you've surely heard about the... arrow functions and the famous special behavior of thisAnd it's also quite likely that your head has exploded more than once trying to understand why. this One thing applies to a normal function and something completely different to an arrow function.

Mastering these two concepts is not just a matter of theory: it's key to writing Modern code in JavaScript and ReactUnderstanding how event handlers work, how context behaves within classes, how asynchronous callbacks are resolved in Node.js, and why certain patterns are considered good practices today.

First of all: a quick review of functions in JavaScript

In JavaScript you can declare functions in several ways, and each way has implications for how they behave. this, in the scope and at the moment the function is available in the code.

Function declaration

A classic function declaration uses the keyword function and a name. These types of functions are "elevated" (hoisting) at the beginning of the scope where they are declared, which allows the function to be called even before its definition in the file.

In a function declared in a traditional way, the value of this is determined according to how The function is invoked: if it is called as a method of an object, if it is used with new, if it is in strict mode, if it switches to call document, Apply o connectiveetc. This flexibility is powerful, but it is also the source of many errors in complex code.

Function expression

You can also assign an anonymous or named role to a variableIn that case, the function itself is not raised, only the variable declaration is raised (with var), but not its value. With years y const You can't even use it before its definition line because of the temporary dead zone.

These functions share the same model of this dynamic Unlike classic declarations: their value always depends on the context of invocation, so they remain sensitive to how you pass them as a callback or how you use them within classes and objects.

How to enable JavaScript on an Android phone
Related article:
How to enable JavaScript on an Android phone

What exactly are arrow functions?

The arrow functions were introduced with ECMAScript 2015 (ES6) as a more convenient, compact, and expressive way to declare functions, especially for callbacks and functional programming with methods like map, filter o reduced.

Its basic syntax consists of a list of parameters, followed by the operator => and then one expression or code blockThis compact format makes the code more efficient. readable in many scenarios where you previously had to write long and repetitive anonymous functions.

Basic syntax of arrow functions

Arrow functions support several syntax variations, all based on the same rules but with shortcuts for simpler cases. Understanding each form helps you write them. cleaner code I can quickly recognize what each callback does.

  • Without parametersEmpty parentheses are used before the arrow and usually an expression after.
    const getMessage = () => "Hola";
  • A single parameterYou can omit the parentheses around the parameter name, which makes the code more concise.
    const square = x => x * x;
  • Several parametersYou always have to use parentheses that enclose them, separated by commas.
    const sum = (a, b) => a + b;
  • Body of a single expressionIf you don't use curly braces, the expression is returned in a certain way. implicit without needing to write return.
  • Multi-line bodyIf you use braces, you have to write return explicitly to return a value, just like in a normal function.

A common source of errors is forgetting the return When you move from a single-line version to a body with curly braces to add more logic, suddenly your function starts returning undefined And it's not obvious why.

Arrow functions and literal objects

If a single-expression arrow function returns a literal objectYou have to wrap that object in parentheses so the engine doesn't mistake it for the beginning of the function block. It's a little syntax trap that catches a lot of people off guard.

Otherwise, the interpreter interprets the keys as an empty block and your function will not return the object, but undefinedThis causes hard-to-trace errors when you try to access properties that were never returned.

Arrow functions and the behavior of this

The truly differentiating factor of arrow functions is not the syntax, but their model of thisWhile traditional functions have a this dynamic Depending on their names, arrow functions have a this lexiconThat is, they capture the This is from the area in which they are created and they keep it unchanged.

This means that within an arrow function, this It cannot be changed with call document, Apply ni connectiveAnd it doesn't depend on whether you use it as a callback in an event handler or pass it to another function. It will always have the same value it had outside of it at the time of its definition.

How does this work in normal functions?

In a function declared with function, the value of this It depends on the context:

  • If you call the function as a method of an object, this Point to that object.
  • If you invoke it without context in non-strict mode, this reference to global object (window in the browser, global in Node.js).
  • If you use new, this it then points to the newly created instance.
  • If you apply call document, Apply o connective, you can force the value of this manually to the object of your choice.

In strict mode, if you call a normal function without an object as a receiver and without using call document or similar, this will undefinedThat's why in many modern examples you see functions that begin with 'use strict' to avoid silent errors and force more consistent behavior.

How does this work in arrow functions?

In the case of arrow functions, this It is not recalculated when invoked; it is taken directly from area where they were definedThat is, they behave similarly to how variables are captured by closure (closure): They look outwards and remember that value forever.

This has several very important consequences for your daily life:

  • You can't use an arrow function like constructor with newbecause they don't have their own this nor the appropriate prototype.
  • Calling an arrow function with call document o Apply will not change the value of thisHowever, you will be able to pass the rest of the arguments normally.
  • In methods of objects created as arrow functions, this It will not point at the object, but at the upper environment (for example, window or the module), which is almost always a bug.

The key takeaway is that arrow functions are ideal when you want the This internal one is the same as the external one.But they're a bad idea when you need a real object method or when you intend to instantiate something with new.

Arrow functions, classes, and React

arrow and this functions in JavaScript

in the world of React and of JavaScript classes in general, the handling of this It's a sensitive issue. The class components inherited from React.Component They use methods in which this must consistently point to the component instance in order to access this.props y this.state.

With methods defined using classic class syntax, the value of this It can be "lost" when passing a method as an event callback, for example to onClickThat's why in many older tutorials you'll see patterns like this.metodo.bind(this) in the constructor or directly in the JSX, something that is considered quite verbose nowadays.

Typical this binding patterns in React

In a class component, there were historically four main ways to ensure that this always points to the component when called from an event handler:

  • Use connective in the method yield, which ensures context but creates a new function in each render.
  • Make the bind in the constructor to avoid recreating the function over and over again, maintaining a single reference.
  • Define the handler as class property with arrow function, taking advantage of the fact that the arrow functions capture the this of the instance.
  • Use arrow function inline in JSX, directly passing an arrow function to the attribute onClick or equivalent.

In practice, the option to declare the handler as class property using an arrow function It became the most convenient pattern: you write less code, you avoid having to do connective manual and this always aim for the component without surprises.

Arrow functions in function components

With the arrival of functional components and hooksReact relies much more heavily on arrow functions. Most modern components are implemented as arrows. const MyComponent = () => { … }This makes the use of arrow functions natural and consistent throughout the tree.

Within these components, it is common to combine arrow functions with other ES6 features such as destructuring, template literals, ternary operators, spread and array methods like map, filter y reduced, thus constructing a very expressive syntax but one that requires good handling of context and immutability.

This in the global context, individual objects and functions

To understand why arrow functions change the game so much, it's worth reviewing how they behave. this in different native JavaScript contexts, both in the browser and in Node.js.

At the global scope of the browser, outside of any function or module, this point to the object windowThat means any variable declared with var At a higher level, it hangs on that global object and is accessible both by name and by window.nameThis is considered bad practice in complex applications due to global pollution.

this within literal objects

When you define a literal object With traditional methods, each method receives a this that refers to the object itself when invoking it as obj.method()This is the standard way to access sister properties with this.property without the need for intermediate variables.

The problem arises when you replace these methods with arrow functionsSince we don't have this ownThe arrow function will capture the external `this`, which is likely the global or module `this`. As a result, you won't be able to access the object's properties within the method using this, completely breaking the original intention of the design.

Advanced use of Sublime Text for quick editing
Related article:
Advanced use of Sublime Text for quick editing

This is within loose functions and strict mode

In traditional functions called "unplugged", without a receiving object, the value of this It depends on whether the code is in strict mode Or not. Strictly speaking, it falls back into the global object, while strictly speaking it becomes explicitly undefinedto avoid dangerous actions on the global environment.

This behavior changes drastically when you encapsulate your code in ES modules, modern bundlers, or IIFEs, where the global can be different or not bound to this in the usual way, especially in Node.js, where the main scope of the archive is that of module and not the global one.

call, apply, bind and their relationship with this

JavaScript offers three widely used methods over classic functions for managing context: call document, Apply y connectiveThey all allow you to explicitly control what value it takes. this when the function is executed.

These methods only work with functions declared with function (or equivalents), since arrow functions ignore any attempt to change their `this`. That's why all three are so useful when working with legacy APIs, old classes, or patterns where you want to reuse the same function on different objects.

call and apply: invoke with explicit this

So much call document , the Apply they execute the function immediatelyThe only difference is how you pass the arguments. In both cases, the first parameter you pass is the object that should be converted. this within the function.

With call document The subsequent arguments are stated one by one, whereas with Apply They pass themselves off as a arrayThis fits very well with situations where you already have the list of arguments as a collection and you want to inject it as is into the call.

bind: create a new function with `this` set

The method connective It does not execute the function at the moment, but instead generates a new feature ’s relationship with the this permanently associated to the object you specify. It's like creating a "specialized" version of the original that will always behave within that context.

This pattern is very common for passing object methods to APIs that will later call them later, for example, as event callbacks, without losing the original context. Before the popularization of arrow functions, connective It was one of the cleanest ways to ensure that a class method continued to point to the correct instance.

Why are arrow functions ideal for callbacks?

One of the reasons why arrow functions have conquered the JavaScript ecosystem is their suitability for asynchronous callbacks and functional programming. When working with APIs like setTimeout, setInterval, promised Or with the Node.js event loop, you often need to access the outside context without it getting distorted.

Before ES6, tricks like saving were used const self = this o const that = this before entering the callback, so that the correct object can still be referenced from within the function. Arrow functions make this unnecessary by automatically capturing the This external and preserve it.

Arrow functions and the event loop in Node.js

En Node.js, where most of the interesting code revolves around Asynchronous I/O (files, network, timers), the combination of arrow functions with the model of event loop and callback queues It simplifies life a lot. Every time you pass a function like a handle to fs.readFileWhether you're sending an HTTP server or any libuv operation, arrow functions help keep references to contiguous variables and module contexts free of surprises.

Furthermore, in microtask queues such as those of promised o process.nextTickIt is very common to write short callbacks like then o catch with arrow syntax, taking advantage of both implicit return such as this lexicon, which makes the code more declarative and expressive.

When should you NOT use arrow functions?

Although arrow functions may seem like the magic solution to all problems, there are scenarios where their use is directly counter-productive and even generates bugs that are difficult to detect.

One of them, perhaps the most important, are the methods of objects or classes that need their own `this`Another is when you want to create builders or use object-oriented patterns supported by prototypes and inheritance, where the absence of prototype and its own context means that arrow functions simply don't fit.

Avoid using arrow functions as object methods.

If you define a method of an object with an arrow function, the This internal will not refer to the objectbut rather to the context in which that object was defined. This breaks the expectations of anyone reading the code and intending to use this.property within the method.

The recommended way to write methods that depend on this It's about continuing to use the classic syntax in literal objects or declaring those methods in a standard way in classes, reserving arrow functions for internal callbacks that need to capture this from a superior method.

Do not use arrow functions as constructors

The arrow functions lack [[Construct]], the internal mechanism that allows a function to be invoked with newTherefore, attempting to use them as constructors will generate an TypeErrorThey don't have their own either. prototypeTherefore, you cannot hang shared methods for instances.

If your design requires reusable instances with common properties and methods, you should opt for Personalized or by traditional constructor functions, leaving arrow functions for stateless logic or purely functional utilities.

Relationship with other modern features of JavaScript

Arrow functions are rarely used in isolation; they almost always go hand in hand with other features of Modern ECMAScript which also affect how you organize and understand code. Some of the most related are years y const, The immutability, the destructuring and spread syntax.

Knowing how to combine them correctly allows you to write very concise arrow functions that transform data with array methods such as map, filter y reducedalways keeping the original state intact, something crucial in environments like React where the immutability pattern is the norm.

Advanced text editing and automation with Notepad++
Related article:
Advanced text editing and automation with Notepad++

let, const and immutability

The arrival of years y const It allowed working with block scopes and with references that are not reassigned. In combination with arrow functions, it is common practice today to declare functions as const myFunction = () => {…}, ensuring that the variable containing the function will not be overwritten.

In data structures such as arrays and objects, immutability is encouraged through the use of Object.freeze when you want to avoid deep changes, and through spread operations and pure methods when you need to create modified copies instead of altering the original, something especially important when rendering or state depends on detecting changes.

Template literals, ternaries and short-circuit

In rendering callbacks, for example in JSX or when constructing HTML strings, it's almost inevitable to mix arrow functions with template literals (backticks) and operators such as the ternary and short-circuit evaluationThese operators allow you to write very expressive inline conditions that fit very well with the declarative style of arrow functions.

Within template literals, the expressions ${…} They are resolved with the same lexical scope that dominates the `this` of arrow functions, so it is essential to be clear about which variables and what context are being used in each interpolation to avoid ambiguous behavior.

Arrow functions in iterations and collections

One of the areas where arrow functions have made the biggest difference is in the processing of arrays and collectionsPassing short callbacks to methods like map, filter, find o reduced It becomes vastly more readable with the arrow syntax.

For example, filtering entries in a list based on a category or transforming API results into internal data structures becomes much more declarative, and combined with destructuring It's easy to extract only the properties you need from within the function's parameters.

map, filter, reduce and company

The typical combination is usually something like this: list.filter(item => condition).map(item => transformation)chaining several arrow functions into array methods. Each of them inherits the `this` from the context where it is defined, although in most of these cases `this` is not even used, but rather explicit parameters, reinforcing the functional style.

En reducedIn this case, where you manage an accumulator that passes from iteration to iteration, arrow functions allow you to express the reduction operation very concisely, although it is advisable not to overuse overly dense expressions if you want the code to remain understandable to other developers.

Imports, exports and modules

In the modern JavaScript ecosystem, almost all code is organized into moduleseither using native syntax import / export or by using bundling systems that compile to that format. In this context, arrow functions fit very well as reusable units of functionality.

It is common to see utilities exported as const myUtil = (…) => {…} and then imported into different files. Since each module is in its own scope, the this lexicon The context of arrow functions tends to be that of the module, which in most pure utilities is not even relevant, since they do not need context.

Best practices with external modules and scripts

When serving JavaScript in an HTML page, it's preferable to load it using external files and avoid embedding large inline blocks. Also, use the attribute type="module" in the script tag enables modern syntax import / export and makes it clear that the file is executed in mode strict default.

Regarding performance, placing the module scripts in the head with defer allows the browser to download and execute the code without blocking rendering of HTML, while maintaining a good user experience. In this whole scheme, arrow functions are simply the most natural way to define small pieces of reusable logic.

Inserting and Manipulating the DOM with Modern JavaScript

Although the focus of this topic is on the arrow and this functions, it is impossible to ignore the role of JavaScript in the DOM manipulationMethods such as getElementById, querySelector, setAttribute or the modification of style They are still basic, but today they are usually combined with arrow callbacks to react to events and update the interface.

Work with event listeners using element.addEventListener('click', e => {...}) This is the standard way to associate logic with user interactions. In these handlers, this Inside the arrow function, it won't be the DOM element, but the external context, so if you need the node, you'll have to use the event parameter itself. e.currentTarget o e.target, instead of relying on this as with the old inline attributes.

Synchronous JavaScript, asynchronous JavaScript, and callbacks

Another major area where arrow functions make a difference is in the asynchronous programmingJavaScript is single-threaded and, to avoid blocking the interface, it delegates slow operations to browser or Node APIs that then call your callbacks when they finish.

Pass arrow functions as callbacks to setTimeout, promisesHTTP requests or disk accesses make the code more compact and maintain context without additional tricks. In the step of nested callbacks towards promised and later towards async / awaitArrow functions have been an essential element in making the data flow readable.

Windows as a thin client
Related article:
Windows as a thin client: configuring Remote Desktop and session policies

Overall, understanding how the arrow functions capture this lexicallyKnowing when they are your best ally and when it's best to continue relying on traditional functions, along with knowing how to combine them with modules, array methods, immutability, classes, and the event loop, is what allows you to write truly solid JavaScript and React; in the end, it's about choosing the right function form for each context, taking advantage of its syntactic benefits without falling into the borderline cases where its `this` behavior could backfire. Share this programming guide and the JavaScript arrow functions.