6

In http://eloquentjavascript.net/1st_edition/chapter6.html, there is the following example:

function negate(func) {
  return function(x) {
    return !func(x);
  };
}
var isNotNaN = negate(isNaN);
alert(isNotNaN(NaN));

Knowing only basic JavaScript and imperative programming, I am stumped by this programming style. What happens during runtime?

I stepped through the code and inspected variables and found that the value of x is NaN. How does it know that the argument to isNaN should be passed as argument x of the anonymous function? In the first place, why does the actual parameter NaN of isNotNaN become the argument to isNaN (ie while isNaN expects an argument, why does it take it from the argument of isNotNaN)?

halfer
  • 19,824
  • 17
  • 99
  • 186
Old Geezer
  • 14,854
  • 31
  • 111
  • 198
  • *"In the first place, why does the actual parameter `NaN` of `isNotNaN` become the argument to `isNaN`"* Because of the third line: `func(x)`. `func` is a reference to `isNaN` and `x` is `NaN`. This is not much different from any other function E.g. in `function foo(x) { return bar(x); }; foo(x)`, `foo` passes `x` along to `bar`. The only difference in your example is that `func` is determined dynamically. – Felix Kling Sep 19 '15 at 15:53
  • **note** this function `negate` correctly work only with functions that takes just one parameter – Grundy Sep 19 '15 at 15:54

3 Answers3

1

Best way to understand this might be with seeing what these things actually equal. Notice how func becomes the passed isNaN function.

function negate(func) {
  return function(x) {
    return !func(x);
  };
}
var isNotNaN = negate(isNaN);
/*

isNotNaN = function(x){
   return !isNaN(x)
}

*/

alert(isNotNaN(NaN));
Kevin Pei
  • 5,800
  • 7
  • 38
  • 55
0

Notice that negate both accepts a function as an argument and returns a function. The returned function will call the argument function, negating the return value. Thus, isNotNaN is a function. When it is called, it will call the function originally passed into negate, in this case isNaN. Whatever you call isNotNaN with will be passed to isNaN.

In essence, you're configuring a function with another function. This might be easier to see with a simpler (no argument function) example:

function addX(x) {
    return function(y) {
        return x+y;
    };
}

var add2 = addX(2);
console.log(add2(2)); // 4

var add3 = addX(3);
console.log(add3(7)); // 10

Now, take this one step further and imagine you passed a function into addX instead of a value.

By the way, this is called currying.

jds
  • 7,910
  • 11
  • 63
  • 101
0

It is because you set this:

   var isNotNaN = negate(isNaN);

And when calling isNotNaN(x)so it is like you call negate(isNaN)(x). You can also use a named function instead of the anonymous here so we say:

    function negate(func) {
      return xValue.call(this, x); //to be in the context of the xValue function
      };
   }
    var isNotNaN = negate(isNaN);
    alert(isNotNaN(NaN));

    function xValue(x) {
       return !func(x);
    }

    But then you have to take care about the context.
mad
  • 1
  • 2