0

I was learning about arrays methods and there's one thing I don't quite understand, possibly related to closures.

First things first though, here's the code snippet:

let range = {
    minNumber: 20,
    maxNumber: 30,
    isValid(number) {
        return number >= this.minNumber && number < this.maxNumber;
    }
};
    
let numbers = [16, 23, 27,  30, 45];
    
let filteredNumbers = numbers.filter(range.isValid, range);
    
console.log(filteredNumbers.length); // 2
console.log(filteredNumbers[0]); // 23
console.log(filteredNumbers[1]); // 27

From what I understand by passing second argument we bind this to range, otherwise simply calling: numbers.filter(range.isValid) will make this undefined. Shouldn't it have access to this either way though, as we're "calling" isValid from range context by using . operator?

There is also a second approach that works:

numbers.filter(number => range.isValid(number))

What's going on here? Now it's able to pick up this from range object all of a sudden? Arrow functions have no this iirc, so it's not that.

Thanks for all the help in advance. :)

Krisztián Balla
  • 19,223
  • 13
  • 68
  • 84
flamasterrr
  • 269
  • 3
  • 12
  • 2
    Does this answer your question? [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – Jared Smith Nov 29 '19 at 17:17
  • 1
    When you call directly with the dot operator `foo.bar()` it picks up the context from the property access. When you pass a method as a callback it loses it: `someFn(foo.bar)`. The def of someFn is going to look something like this: `const someFn = f => f();` where it calls the argument (bound to f in this case) without the dot operator (i.e. no context). The fact that you're passing an arrow function is irrelevant, it's about when the property lookup occurs. – Jared Smith Nov 29 '19 at 17:19
  • 1
    I moved your first approach into an executable snippet and it gives the desired result. Which I was expecting, because you did everything right. – Krisztián Balla Nov 29 '19 at 18:01

1 Answers1

1

My understanding is that when you call

numbers.filter(range.isValid)

really what you are doing is passing the function by referrence or

numbers.filter(isValid(number) {
    return number >= this.minNumber && number < this.maxNumber;
  })

effectively the context is not transferred(e.g. maxNumber and minNumber are not set anywhere.)

when you do

numbers.filter(number => range.isValid(number))

it works because the => function does not establish a context so it gets the context from the definition of range in the current scope.

when you do

numbers.filter(range.isValid, range)

You are making use of the optional parameter to .filter described below(From W3C docs here:https://www.w3schools.com/jsref/jsref_filter.asp)

Optional. A value to be passed to the function to be used as its "this" value. If this parameter is empty, the value "undefined" will be passed as its "this" value

ruby_newbie
  • 3,190
  • 3
  • 18
  • 29