Is it possible to transform an arrow function into a regular function?

Currently, I am working my way through the AJAX type ahead course in Javascript 30 by Wes Bos. As I progress through this course, I have made a conscious effort to minimize my use of ES6 features for the sake of honing my skills.

If you want to see the finished version, you can check it out here: https://codepen.io/Airster/pen/KNYXYN

A key feature of this project is that as you type in a location, the matching value will be highlighted in yellow within the search results.

The magic behind this functionality lies in the displayMatches function:

var displayMatches = function(){
    console.log(this.value);
    var matchArray = findMatches(this.value, cities);
    var html = matchArray.map(function(place){
        console.log(this, place, "test");
        var regex = new RegExp(this.value, 'gi');
        var cityName = place.city.replace(regex, "<span class='hl'>" + this.value + "</span>");
        var stateName = place.state.replace(regex, "<span class='hl'>" + this.value + "</span>");
            return `
          <li>
            <span class="name">${cityName}, ${stateName}</span>
            <span class="population">${place.population}</span>
          </li>
            `;
    }).join("");
    suggestions.innerHTML = html;
}

I encountered an issue with the variable html. My current approach using a regular map function like this:

var html = matchArray.map(function(place){...})
didn't yield the desired results and returned undefined.

However, when I switched to using an arrow function:

var html = matchArray.map(place => {...})
the function worked effectively and the searched value was highlighted properly.

I'm curious why, in this specific context, only the arrow function seemed to work. Any insights would be greatly appreciated!

Thank you in advance!

Answer №1

Could someone please shed light on why the arrow function works in this specific context?

An arrow function, as per its definition, retains the current lexical value of this, unlike a regular callback function. When you use a regular callback function, you lose the correct value of this that your code relies on.

To rectify this issue, you can either utilize an arrow function to maintain the current scope value of this, or pass the optional argument thisArg to .map() immediately after the function.

In the documentation for Array.prototype.map by MDN, the optional thisArg parameter is explained:

var new_array = arr.map(function callback(currentValue, index, array) {
    // Return element for new_array
}[, thisArg])

To retain this in your code without using an arrow function, you can pass the thisArg to your call to .map().

var displayMatches = function(){
    console.log(this.value);
    var matchArray = findMatches(this.value, cities);
    var html = matchArray.map(function(place){
        console.log(this, place, "test");
        var regex = new RegExp(this.value, 'gi');
        var cityName = place.city.replace(regex, "<span class='hl'>" + this.value + "</span>");
        var stateName = place.state.replace(regex, "<span class='hl'>" + this.value + "</span>");
            return `
          <li>
            <span class="name">${cityName}, ${stateName}</span>
            <span class="population">${place.population}</span>
          </li>
            `;
    }, this).join("");              // <== See the this argument passed here
    suggestions.innerHTML = html;
}

For the sake of completeness, there are six possible solutions available:

  1. Pass the this argument to .map() as demonstrated above
  2. Use an arrow function (ES6 only)
  3. Utilize .bind(this) with the callback function
  4. Create a local variable var self = this; and reference self instead of this
  5. Use let val = this.value before the callback and then simply employ val within the callback
  6. Declare the e parameter to the event handler and utilize e.target.value rather than this.value, eliminating the need for this altogether

Considering that .map() inherently offers the desired functionality for this, if you opt not to use an ES6 arrow function, it would be prudent to leverage the .map() argument. However, all six solutions provided will effectively address the issue.


Upon scrutinizing how you engage with this in your callback, it appears that you solely access

this.value</code. In this scenario, you could alternatively declare <code>let val = this.val
before the callback and exclusively utilize val inside the callback, thereby obviating the necessity to employ this altogether.

var displayMatches = function(){
    let val = this.value;
    console.log(val);
    var matchArray = findMatches(val, cities);
    var html = matchArray.map(function(place){
        console.log(this, place, "test");
        var regex = new RegExp(val, 'gi');
        var cityName = place.city.replace(regex, "<span class='hl'>" + val + "</span>");
        var stateName = place.state.replace(regex, "<span class='hl'>" + val + "</span>");
            return `
          <li>
            <span class="name">${cityName}, ${stateName}</span>
            <span class="population">${place.population}</span>
          </li>
            `;
    }).join("");
    suggestions.innerHTML = html;
}

Answer №2

One issue arises when comparing arrow functions to regular functions, as the this variable gets reset when creating a new function. To prevent losing the value of this, it is advisable to store it in a new variable within the function.

var displayMatches = function(){
    console.log(this.value);
    var that = this;
    var matchArray = findMatches(this.value, cities);
    var html = matchArray.map(function(place){
        console.log(that, place, "test");
        var regex = new RegExp(that.value, 'gi');
        var cityName = place.city.replace(regex, "<span class='hl'>" + that.value + "</span>");
        var stateName = place.state.replace(regex, "<span class='hl'>" + that.value + "</span>");
            return `
          <li>
            <span class="name">${cityName}, ${stateName}</span>
            <span class="population">${place.population}</span>
          </li>
            `;
    }).join("");
    suggestions.innerHTML = html;
}

Answer №3

When utilizing the this variable, it is important to note that within a function, it may refer to the function itself rather than the intended object.

To avoid this issue, storing the value in another variable before usage can be a solution.

const value = this.value;
const html = matchArray.map(function(place) { 
const regex = new RegExp(value, 'gi'); 
   ...
});

In the realm of JavaScript, functions are regarded as first-class objects, as they possess properties and methods akin to any other object. Consequently, when using the this variable within a function (be it anonymous or not), it will generally reference the function itself instead of the class object, which is customary in Object Oriented Programming.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Lexical_this

Arrow functions, however, do not bind the this variable.

An arrow function does not create its own this, the this value of the enclosing execution context is used.

To observe this concept firsthand in your codepen, visit: https://codepen.io/BoyWithSilverWings/pen/VMXyXB?editors=0010

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What could be the reason for the malfunction of my callback function?

This particular function is executed with the command node server const express = require("express"); const app = express(); const fs = require("fs"); const connectDb = require("./config/db"); const __init__ = (local = false) => { fs.writeFile( ...

Steps to hide a div with jQuery when a user clicks outside of a link or the div itself:1. Create a click event

Here's a simple question - I have a link that when clicked, displays a div. If the user clicks away from the link, the div is hidden, which is good. However, I don't want the div to be hidden if the user clicks on it. I only want the div to disap ...

Tips for avoiding repeated Modal Popup instances and preventing the page from automatically scrolling to the last element when using ReactJS

I've been working on a project where I'm fetching data from a server and displaying 10 different sets of data in Bootstrap cards using the map() function. Each card contains a button to open a modal, along with a Link that shows the route related ...

Tips for iterating through a collection of arrays with jQuery

I am facing an issue with looping through an array of arrays and updating values or adding new keys to each array. Here is my current setup: var values = []; values['123'] = []; values['456'] = []; values['123&apo ...

Is there a way to determine if npm packages are accessing and misusing my system's environment variables?

Apologies if this seems nonsensical. But including a code snippet like this: // to illustrate I'm utilizing a source from https://www.npmjs.com/package/got got.post(maliciousUrl, {json: process.env}) Is it enough to leak environment variables to an u ...

Executing various count() queries on a MongoDB using mongoose

Hey there, I consider myself a MongoNoob and I know this question has probably been asked before in various ways, but I haven't found a specific solution yet. Let's imagine I have two Moongoose Models set up like this: var pollSchema = mongoose. ...

What is the best way to set the keyframes CSS value for an element to 0%?

Would like to create a progress indicator div using CSS animations? Check out the JSBin link provided. The desired behavior is to have the div width increase from 30% to 100% when the user clicks on stage3 after stage1, rather than starting from 0%. Althou ...

ng-repeat not functioning properly when using track by, filter, and orderBy

I have come across this code snippet. http://jsfiddle.net/0tgL7u6e/ Scripting in JavaScript var myApp = angular.module('myApp',[]); function MyCtrl($scope) { $scope.nameFilter = ''; $scope.contacts = [ {name: 'G ...

Can you use ng-show within ng-if in Angular?

How can I make this input only show a property is true per the ng-if? The current code looks like this: <input type="button" class="naviaBtn naviaBlue" ng-if="ppt.Globals.hasDebitCard" ng-click="alertShow = (alertShow == 2 ? -1 : 2)" value="outstandin ...

Angular - Error: Cannot read property 'publishLast' of undefined

My goal is to prevent multiple requests from being created when using the async pipe. I am facing an issue with a request to fetch a user from the API: getUser() { this._user = this.http.get<User>(environment.baseAPIUrl + 'user') ...

Oops! An error occurred: fs.readFileSync is not a valid function to perform the Basic

I'm facing a dilemma - I have a relatively simple app (I'm still new to Node): App.js import * as RNFS from 'react-native-fs'; var config_full; // readFile(filepath: string, encoding?: string) RNFS.readFile('./config.toml', ...

Transforming the text color after clicking on it on Squarespace - here's how to do it!

How can I make text change color upon clicking in Squarespace? Where should I place the block id in the code to target a specific block? Check out the code snippet I've been experimenting with: <script> document.getElementById('chang ...

Switch between divs based on the current selection

var header = $("#accordion"); $.each(data, function () { header.append("<a id='Headanchor' href='javascript:toggleDiv($(this));'>" + this.LongName + "</a>" + "<br />", "&l ...

Guide on accessing text content from a sibling div element using jQuery

There are several divs arranged on a page as shown below. If a user clicks a link within the UL .list-links, I want to capture the content inside: <span class="asa_portlet_title">Title here</span> and store it in a variable. I have attempted ...

The issue of array sorting, specifically the function(a, b) {return b.value - a.value), is causing some

I am struggling to retrieve data from firebase and sort them according to the field 'overallScore' in descending order. Despite following suggestions like using array.sort(function(a, b) {return b.value - a.value), I have not been successful in s ...

Perform the function prior to making any adjustments to the viewmodel attributes

Within my view, I am showcasing employee details with a checkbox labeled Receive Daily Email. When a user interacts with this checkbox, I want to trigger an AJAX function to validate whether the user is allowed to modify this setting: If the result is tru ...

How come the method $.when().pipe().then() is functioning properly while $.when().then().then() is not working as expected

I'm still grappling with the concept of using JQuery's Deferred objects, and am faced with a puzzling issue. In this code snippet, my attempt to chain deferred.then() was unsuccessful as all three functions executed simultaneously. It wasn't ...

React - Issue with Input event handling when utilizing both onChange and onKeyDown functions

I was attempting to create a feature similar to a multi-select, where users could either choose a value from a list or enter a new value. The selected value should be added to an array when the user presses the enter key. To monitor changes in the input ...

Choose the current parent item using Angular's ng-selected and $index

http://plnkr.co/edit/fwwAd4bn6z2vxVN2FUL7?p=preview On the Plunker page linked above, I am trying to achieve a specific functionality. I would like the 3 dropdown lists to display values A, B, and C initially. When a 4th dropdown option is added, it shoul ...

Deleting a specific row within a Material UI DataGrid in Reactjs: Tips and tricks

As I embark on this React project, things are progressing smoothly. However, a challenge has arisen. The functionality I aim for is as follows: When the checkbox in a row is clicked, I want that particular row to be deleted. For instance, selecting checkb ...