javascript - Converting arrow function to regular function? - Stack Overflow

admin2025-04-21  0

I'm going through Javascript 30 by Wes Bos.

I'm doing the AJAX type ahead course, and I'm sort of trying to limit the amount of ES6 I do on it, just for the sake of practice.

This is the finished version:

Note that as you type in a location, the searched value gets highlighted in yellow within the results.

Now the javascript that determines this is 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;
}

The issue lies with the function variable html. Currently I have it as:

var html = matchArray.map(function(place){...})

This doesn't work, and I will get undefined in the results.

However, if I change it into an arrow function:

var html = matchArray.map(place => {...})

The function will run and the searched value will get highlighted.

Can someone explain why within this particular context, the arrow function works?

Thanks in advance!

I'm going through Javascript 30 by Wes Bos.

I'm doing the AJAX type ahead course, and I'm sort of trying to limit the amount of ES6 I do on it, just for the sake of practice.

This is the finished version:

https://codepen.io/Airster/pen/KNYXYN

Note that as you type in a location, the searched value gets highlighted in yellow within the results.

Now the javascript that determines this is 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;
}

The issue lies with the function variable html. Currently I have it as:

var html = matchArray.map(function(place){...})

This doesn't work, and I will get undefined in the results.

However, if I change it into an arrow function:

var html = matchArray.map(place => {...})

The function will run and the searched value will get highlighted.

Can someone explain why within this particular context, the arrow function works?

Thanks in advance!

Share Improve this question asked Oct 9, 2017 at 5:18 user4609276user4609276 3
  • Hard to tell, must be the lexical scoping. Your this value probably isn't what you think. – Rafael Commented Oct 9, 2017 at 5:26
  • try using a transpiler :p – Jaromanda X Commented Oct 9, 2017 at 5:27
  • even more fun would be to turn the outer function into an arrow function. As your this.value is only used, consider if you couldn't make it an argument of the method, which would make a lot more sense – Icepickle Commented Oct 9, 2017 at 8:40
Add a ment  | 

3 Answers 3

Reset to default 5

Can someone explain why within this particular context, the arrow function works?

An arrow function (by definition) preserves the current lexical value of this. A regular callback function does not. So, when you use the regular callback function, you lose the proper value of this which your code depends on.

You can fix that by either using an arrow function which preserves the current scope value of this or by passing the optional thisArg to .map() as an argument right after the function.

From the MDN doc for Array.prototype.map, you can see the optional thisArg:

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

So, to make your code preserve this without an arrow function, you can pass the thisArg to you 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;
}

FYI, for pleteness, there are actually six possible solutions here:

  1. Pass the this argument to .map() as shown above
  2. Use an arrow function (ES6 only)
  3. Use .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 just use val inside the callback (code shown below).
  6. Declare the e parameter to the event handler and use e.target.value instead of this.value, removing the need to use this at all.

Since .map() has the desired feature for this built-in, if you're not going to use an ES6 arrow function, then it makes the most sense to me to use the .map() argument, but all of these four solutions will work.


Looking at the details of what you're doing with this in your callback, you are only reading this.value so you could also just grab let val = this.val before the callback and just use val inside the callback too - eliminating the need to even use this at all.

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;
}

The problem is that unlike in an arrow function the this variable is being renewed for the new function. You should store the value of this in a new variable so you don't lose it in 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;
}

It is the use of this variable. When you are using this inside a function, it will start to refer to the function itself rather than the one you intend it to.

Saving the value to another variable before use will do the trick for you.

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

In JavaScript, functions are first-class objects, because they can have properties and methods just like any other object. As a side effect of this, using the this variable inside a function (anonymous or otherwise) will result in it referring to itself rather than the class object which is mon in Object Oriented Programming.

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

Arrow functions do not bind this variable.

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

Here is how that will work out in your codepen. https://codepen.io/BoyWithSilverWings/pen/VMXyXB?editors=0010

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745177458a288993.html

最新回复(0)