Just like numbers, strings, booleans, arrays, and objects
Anywhere you can assign or otherwise use a value, you can assign or use a function as the value.
This is the case we’re used to.
const add = (a, b) => a + b;
The const add
just declares a variable.
The =
assigns it a value.
And the value is the function (a, b) => a + b
.
const anotherName = add
This is evaluated by first evaluating the variable
add
which gives us the function value.
Note it does not call the function; it just gets the value.
That value is then assigned to the variable
anotherName
.
We can also use functions without giving them a name.
One scenario we’ve seen recently for assigning a function to the property of an object is to register an event handler.
const b = document.querySelector('button');
b.onclick = (e) => {
console.log('Button was clicked');
};
This works because when a button in a web page is clicked, the
browser will find the value of the button object’s
onclick
property and call it.
We can also define a named function
const clicked = (e) => {
console.log('Button was clicked');
};
And then use it as an event handler
const b = document.querySelector('button');
b.onclick = clicked;
Once again, note no ()
s after clicked
as
we are not calling it, we are just getting its value.
So far this year we’ve worked with functions that take numbers, booleans, strings, arrays, and objects as arguments and return them as values.
Can we pass a function as an argument to another function? Can we return a function as a value?
Functions that take functions as arguments or return functions as a values, are called “higher-order functions” or “HOFs”.
Consider these two functions:
const addTwo = (number) => number + 2;
const addThree = (number) => number + 3;
Those are obviously silly. But there’s a pattern which we can capture with this function:
const addN = (number, n) => number + n;
I.e. we use another argument to abstract the difference between the two earlier functions.
const firstEven = (numbers) => {
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) return numbers[i];
}
};
const firstOdd = (numbers) => {
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 !== 0) return numbers[i];
}
};
What’s the same?
The criteria:
numbers[i] % 2 === 0
vs
numbers[i] % 2 !== 0
Unlike in addTwo
and addThree
the
difference is not just a value; it’s code.
Define a function to capture the even test:
const isEven = (n) => n % 2 === 0;
And rewrite firstEven
to use it:
const firstEven = (numbers) => {
for (let i = 0; i < numbers.length; i++) {
if (isEven(numbers[i])) return numbers[i];
}
};
Works just the same as before. But now it’s calling a function to check the criteria.
const firstEven = (numbers, p) => {
for (let i = 0; i < numbers.length; i++) {
if (p(numbers[i])) return numbers[i];
}
};
Called like this:
firstEven(numbers, isEven)
Note that it’s isEven
not isEven()
. We’re
not calling isEven
; we’re passing it as a value.
const firstMatching = (numbers, p) => {
for (let i = 0; i < numbers.length; i++) {
if (p(numbers[i])) return numbers[i];
}
};
Call as before:
firstMatching(numbers, isEven)
Or with anonymous functions.
firstMatching(numbers, (n) => n % 2 === 0) // first even
firstMatching(numbers, (n) => n % 2 !== 0) // first odd
We can also write functions that return functions:
const adder = (n) => {
return (x) => x + n;
};
This function takes an argument, n
, and returns a new
function that takes a single argument and return that argument added
to n
.
adder
This lets us define new functions like this:
const addTwo = adder(2);
const addThree = adder(3);
And we can use them just like before:
addTwo(5) ⟹ 7
addThree(5) ⟹ 8
const complement = (f) => {
return (x) => !f(x);
}
What does this do?
firstMatching(numbers, complement(isEven));
Another way to find the first odd number.