I'm having a hard time understanding exactly what is happening in the code here and how this script is changing other functions.
This is taken from eloquentjavascript.net chapter 5 on higher order functions.
function noisy(f) {
return function(arg) {
console.log("calling with", arg);
var val = f(arg);
console.log("called with", arg, "- got", val);
return val;
};
}
noisy(Boolean)(0);
// → calling with 0
// → called with 0 - got false
-
1The code edit was Eric. I was more concerned about the "Can you explain higher order functions to me" being something that people might answer without trying to answer the bit about this code... and that question is a bit on the broad side.user40980– user409802016年01月28日 23:04:25 +00:00Commented Jan 28, 2016 at 23:04
4 Answers 4
Since it's not clear what part of this is confusing you, let's take this step by step.
1) Boolean
is a function. In this case, it takes one argument, and returns either true or false depending on whether the argument was truthy or falsy. So if it helps, you could replace Boolean
with function(x) { return !!x; }
and get roughly the same behavior.
2) This line:
noisy(Boolean)(0);
is interchangeable with:
var func = noisy(Boolean);
func(0);
Assuming the identifier func
is not used anywhere else in your code.
3) noisy(Boolean)
obviously calls the noisy
function, with f
set to the function Boolean
. The call to noisy
then returns a function like this:
function(arg) {
console.log("calling with", arg);
var val = Boolean(arg);
console.log("called with", arg, "- got", val);
return val;
}
4) The function returned by noisy
is then called with 0
as the value of arg
. That effectively does the following:
console.log("calling with", 0);
var val = Boolean(0);
console.log("called with", 0, "- got", val);
return val;
5) If #1 made sense, then it shouldn't be surprising that Boolean(0)
evaluates to false
. From there it should be obvious why the output is what it is.
Consider the line noisy(Boolean)(0);
Boolean
is a javascript function that takes an optional parameter.
The first part of the line in question, noisy(Boolean)(0); , takes the Boolean function, and passes it into the noisy
function as a parameter.
The noisy
function returns a (new) function that takes a parameter (return function(arg)
).
The second half of the line in question, noisy(Boolean)(0); , takes the return value of noisy (a function that takes a parameter) and invokes it with the parameter 0
;
The function that noisy
returns will do a couple of console writes, but in between will invoke the function that was originally sent into noisy
(the Boolean
function, called f
) with the parameter (0
, called arg
), and then capture and pass on Boolean's return value through the variable val
.
I think that it is misleading to say that this is changing any function. This code is creating a new function that wraps an existing function with extra functionality.
In this case, this code simply wraps the Boolean
function with a couple of console.log calls, but the Boolean
function remains unchanged.
First keep in mind that this code is not changing/mutating the passed in function. Passing in the Boolean constructor is a bit odd and may lead to some confusion so lets remove it and break it down more for clarity.
var noisyIsArray = noisy(Array.isArray);
noisyIsArray([]);
> calling with []
> called with [] - got true
true
noisyIsArray(2);
> calling with 2
> called with 2 - got false
false
In its simplest form noisy can take any function and return a function. Inside of the function that it returns it calls the function that it takes in. Before calling it, it just logs the arguments and returns value.
In the above case Array.isArray
is the function that we are passing into noisy
and var noisyIsArray
is a function as well (the function returned by noisy).
Here is a very simple (and not robust) higher order function :
function logSomething() {
console.log('something');
}
function invoke(fn) {
return function() {
fn();
}
}
var invokeLogSomething = invoke(logSomething);
invokeLogSomething();
>something
All invoke
does is take a function and return a function that calls the passed in function. There is nothing special about the fn
function call is just like any other one.
This JavaScript snipped implements and show two particular points :
- a Closure: that is a special habitability of some languages to capture a variable into another context.
- the fact that in Javascript any variable is an object (a string, an integer, a function, a class, ...)
This snipped finally provide a function noisy() that can run another function with extra debug information.
Let's see step by step :
function noisy(f)
This function takes a function as argument. No problem: with JS a function is also an object.
return function(arg) {
The noisy() function returns another function without a name (an anonymous function). Still no problem: with JS a function is also an object.
You can notice that the argument f
is not use anywhere in function noisy(). It is used only in the anonymous function. If you don't know about Closure you should think that argument f
is superfluous in noisy(), and that it should raise an error in the anonymous function since it is not declared a local nor global. In fact, because of the Closure feature, argument f
is actually passed to the anonymous function as a variable, and this variable is closed in the context of the function. That is the variable f
is no more linked to argument f
after the anonymous function is actually created.
The other part of the functions is quite simple : the anonymous function run the function f
and log some debug info in the console, before and after.
The last part of the snipped is a test of the function.
noisy(Boolean)(0);
It is tested with the class Boolean. This is a strange choice for a test, but is works because in JS a class is an object. Calling a class as a function simply creates a new object of that type. Another well know example is Date('2016-01-01')
.
-
In Javascript, almost everything is a function, not an object.Adam Zuckerman– Adam Zuckerman2016年01月28日 23:39:34 +00:00Commented Jan 28, 2016 at 23:39
-
1@AdamZuckerman A function is always an object (it has methods
call()
andapply()
). While a string is not, an integer is not, .... try("hello")()
and you will have an errorTypeError: "hello" is not a function
.Skrol29– Skrol292016年01月28日 23:54:26 +00:00Commented Jan 28, 2016 at 23:54 -
1In javascript, Boolean is an object of type 'function', but it's not a class.Eric King– Eric King2016年01月29日 00:20:21 +00:00Commented Jan 29, 2016 at 0:20