The previous chapter was very conceptual. Loops are tricky
to get a hang of, but once you visualize how looping can solve problems,
you’re definitely on your way to thinking algorithimically, which is to say,
programmatically. This chapter is a bit more focused, but it builds on the
idea of a block of code, like the loop or the if statement. Blocks in
JavaScript are always surrounded by braces ({}
), and that is true for
functions, as well.
Function, function, what’s your…
Just as looping is useful because it automates repetitive tasks, functions break up your code into smaller pieces. This means that it’s easier to reason about your work abstractly and find problems. Additionally, functions also automate repetitive tasks, and that’s the way I’ll introduce them to you.
Imagine if you want your computer to make burritos. Wouldn’t it be great to just tell it “make a burrito” every time you wanted one, instead of saying “get out the tortillas, take one out, place it on the griddle,” and so on? We make little tasks in our daily life abstract using the huge figurative power of language as well as our own memories. Computers can’t think as abstractly, however. And though they do have memories, we still need to spell things out in detail. Yet once we do it, we’re set. We can ask the computer to make us a burrito from then on.
Of course, I don’t think computers can make good burritos, but stick with me.
All those burrito-making instructions can be collapsed into a function. That might look something like:
// This code is for illustration only. It will crash your console.
//
let makeABurrito;
makeABurrito = function(){
prepareTortilla();
addBeans();
addOnionsAndCilantro();
// etc.
rollUpTortilla();
}
As you can see, there are syntactic similarities between functions and loops.
Both use blocks with braces, and both have parentheses. In fact, this snippet
of code is perfectly fine JavaScript. That’s because the makeABurrito()
function does nothing but call other functions. We can imagine that a
function like prepareTortilla()
has even more specific steps inside it. But
with the function in place, you just have to execute makeABurrito()
once and
be done with it. The internals of the function take care of everything else.
Quickly before moving on, that line // etc.
is a comment. Comments are
very useful in programming because they can serve as little messages to
yourself (or to other programmers) about what is going on in your program.
In JavaScript, everything after two slashes (//
) to the end of the line is
commented out. If you want to comment out a whole section of multiple lines, begin
it with /*
and close it with */
. Or you can put a //
in front of every
line. I’ll start commenting the code I provide, where necessary.
Parameters
Back to the function. Did you notice the parentheses that follow the function
name? Where have you seen this kind of of syntax before? We’ve already gone
over prompt()
, for example, in the previous chapter, and
that is, of course, a function.1 But recall how we typed it:
let userInput;
userInput = prompt("What do you want to have for dinner?", "Type your answer here.");
The parentheses aren’t empty. Instead, they have two strings in them. From
experience, we know that that the first string is what appears at the top of
the prompt box, and the second string is what appears where we type our
answer. We can abstract out the function, then, as prompt(promptText,
defaultText)
, where promptText
and defaultText
are two variables. And, in
fact, if we were to rewrite the above as:
let promptText, defaultText, userInput;
promptText = "What do you want to have for dinner?";
defaultText = "Type your answer here.";
userInput = prompt(promptText, defaultText);
It would work in exactly the same way. These two variables, promptText
and
defaultText
are parameters that we send to the function.2 Giving
functions parameters lets us change the internals of the function to let it
react to specific instances. Now sometimes I want black beans in my burrito,
and sometimes I want pinto beans. Let’s add a parameter to makeABurrito()
to
let us specify which beans to use on the fly:
// This code is for illustration only. It will crash your console.
//
let makeABurrito;
makeABurrito = function(beansVariable){
let beansResponse;
prepareTortilla();
addBeans();
addOnionsAndCilantro();
beansResponse = "You ordered " + beansVariable + " beans. Good choice!";
$("#response").html(beansResponse);
// etc.
rollUpTortilla();
}
If we were to execute:
// This code is for illustration only. It will crash your console.
//
let blackBeans;
blackBeans = "black";
makeABurrito(blackBeans);
We would see that the webpage would now read “You ordered black beans. Good
choice!” Don’t actually try this, yet, though. Can you see why that is the
case? We define a variable, blackBeans
and assign it to the string "black"
.
Next, we send (or pass) that variable as a parameter to makeABurrito()
.
Now, inside the function, we see that it makes reference to a beansVariable
,
that has the value “black,” which it then prints in #response
, like we did last
chapter.
But where did beansVariable
come from? And how did it get set to “black”?
There’s no beansVariable = "black"
, after all. The answer is that the
variable is defined at the same time as the function is. makeABurrito =
function(beansVariable){}
defines both the function, makeABurrito()
, and
the parameter, beansVariable
, which can be used as a variable inside the
function.
The number of parameters you can define is up to you. Imagine you had different kinds of tortillas, like whole wheat and regular wheat. You can redefine the function as:
// This code is for illustration only. It will crash your console.
//
let makeABurrito;
makeABurrito = function(beansVariable, tortillaVariable){
prepareTortilla(tortillaVariable);
addBeans(beansVariable);
addOnionsAndCilantro();
// etc.
rollUpTortilla();
}
What would happen if you executed makeABurrito("black", "whole wheat")
?
Notice how this looks rather similar to prompt(promptText, defaultText);
?
Back to numbers
makeABurrito()
is a great function, and it’s making me hungry, so let’s
abandon it for a bit and go back to using numbers. Back in Chapter
2, we made a tipping calculator. We can build on that example
with real, usable code.
First, what information do you need in order to know how much to tip? You need
to know the total
and the tipRate
, which is a percentage, like 15 or 20%.
The barebones function looks like this, then:
let tipCalculator;
tipCalculator = function(total, tipRate){
// 1. Calculate the percentage of the total
// as a variable “tipAmount”
//
// 2. Change #response to tell us the tip
// amount.
}
In fact, type this into your scripts.js
file. It can go after the earlier
code you’ve written, or it can replace it. Note what’s in the comments; it’s a
sketch of what the function will do. The first step is that it’ll do some
math—an easy calculation. In the second step, it will tell us what the result
of the calculation is.
let tipCalculator;
tipCalculator = function(total, tipRate){
// step 1:
let tipAmount;
tipAmount = tipRate * total;
// and step 2:
$("#response").html("Your tip is $" + tipAmount);
};
// Now call (or “execute”) the function, passing a
// total of $50.00 and a tipRate of 20%:
tipCalculator(50.00, 0.2);
Save and reload, and the webpage should now inform you that you owe $10. If it does, commit. In the exercises, we’ll expand on this function.
Scope
Alongside the idea of a function block, that is, the set of curly braces, we also have the idea of scope. Beginner programmers often get tripped up by scope, but that’s ok, so do veteran programmers. Note that though conditionals and loops also use blocks, they don’t affect scope in the same way.
Just like a “scope” is used to see things that are far away (telescope) or
are really tiny (microscope), in JavaScript, scope also has to do with
visbility. Variables defined with the let
keyword have block-level
scope.3 That means:
> let global;
> global = "🌏";
> if (true) { console.log(global); };
//--> 🌏
> let globalFunction;
> globalFunction = function(){ console.log(global); };
> globalFunction();
//--> 🌏
When we define global
, it’s visible to us inside subsequent if statements
and functions. But notice this:
> let global;
> global = "🌏";
> if (true) {
let blocky;
blocky = "📓";
console.log("global is " + global);
console.log("blocky is " + blocky);
}
//--> global is 🌏
//--> blocky is 📓
> console.log("Wait, the value of blocky is really " + blocky + "?");
This last line will crash with a ReferenceError
. blocky
, it turns out, is
only visible within the if statement block. Once the code leaves the {}
,
blocky
is no longer available. The same works with functions:
> let global;
> global = "🌏";
> let showMeABurrito = function(){
let burrito;
burrito = "🌯";
global = "I’m global!";
console.log("global is " + global);
console.log("burrito is " + burrito);
}
> showMeABurrito();
//--> global is I’m global!
//--> burrito is 🌯
> console.log(global);
//--> I’m global!
> console.log("Wait, the value of burrito is really " + burrito + "?");
Crash. Again. Sadly, burrito is not defined
. But notice that global
was
changed inside the function, and that change persisted outside the
function. global
is visible—and, as a result, changeable (or
mutable)—everywhere. But burrito
is not.
As Molly Bloom asks, “who’s he when he’s at home?” and we may, also, ask,
“where are we when we’re in the console?” That is, if burrito
is defined in
the function block, where on earth is global
defined? Even though we talk
about typing “in” the console, we’re actually always within a special object
called window
. It is the window of the
browser, and when we open up
the console, we’re getting closer access to that window. In fact, prompt()
and alert()
, two functions you’ve already seen, are actually methods that
belong to window
; window.alert()
and alert()
will do the same thing. But
more on methods next chapter. The window
is typically
ignored, as it’s the frame that is unavoidable. Its ubiquity gives it the
privilege of being silent.
Back to the purpose of this section. Scope helps you keep your code tidy,
because there is less risk of variables’ being accessed where they shouldn’t
be. Just remember, whenever you type let
to define a variable inside a
function, that variable is only available inside that function.
In closing, functions are powerful things, as we can see. And though,
ultimately, the goals of this course are not to write code that is as modular
as the use of functions would make possible, you will still be typing the word
function
a lot.
Exercises
- Add functionality to the tip calculator so that you can enter “20” or “.2” for 20%, and the calculator understands the difference.
Footnotes
-
console.log()
also looks like a function, but as I mentioned back in chapter 2,.log()
is a method belonging to theconsole
object. ↩ -
Parameters are also often called arguments, but to my ears, that term is more opaque. ↩
-
This is one difference between
var
andlet
, but I’m not teachingvar
except for those of you with old browsers. Other than the slightly contrived examples in this section, my examples won’t be making a lot of use of block-scoping. ↩