Elements of Programs

It’s time to turn from the big ideas about programming—what is computing, what can we do with programming, and how to we turn our ideas into programs—to technical aspects of programs that enable us to craft correct and efficient software applications. We now need to ask: What are the components out of which we create programs? We’ll need to know what they are, so we have a vocabulary to talk about, and do, programming. It is not enough to guess; we need to know, we need to know precisely.

Values

Programs manipulate information. Information is represented with values.

Values are the information units we manipulate with programs. Values can be atomic or composite, where composite means they are composed of other values (known as their components). Let’s just see examples for now; explanations will be given in class, and deeper theory will follow when we get to data types later. The idea for now is to get a kind of “big picture” for the kind of values we can manipulate.

-8993
2.78E129
true
false
"Hello, how are you today? 😀"
['Sparky', 'Spot', 'Spike', 'Luna']
{ size: 4, brand: 'DKNY', price: 249.33, mainColor: 'turquoise' }
word => word + 's'
(x, y) => 3 * x / y
undefined
null
Number.POSITIVE_INFINITY
NaN
Symbol('stop')
/\d{5}(-\d{4})/

Types

Details will come later, but in class we are going to classify the values above, and more into:

You’ve seen most of these in prior code-alongs; now is the time to look at them more deeply.

Expressions

So values are the units of information. How do we compute new values?

An expression is a fragment of code that computes a value. An expression may be just a single value, or it is formed by applying operators to expressions. Examples:

2                         → 2                  (just a value)
3 + 8.1 * 5               → 43.5               (addition and multiplication)
(3 + 8.1) * 5             → 55.5               (parentheses)             
9 > 3                     → true               (greater than)
9 > 3 && 1 === 2          → false              (logical AND)
9 > 3 || 1 === 2          → false              (logical OR)
"dog" + "house"           → "doghouse"         (string concatenation)
"Hello".length            → 5                  (string length)
"Hello".replace("e", "u") → "Hullo"            (string replace)
[1,1,2,3,5].join("+")     → "1+1+2+3+5"        (string joining)
(x => x / 2)(9)           → 4.5                (function application)
`Now ${100 - 1} problems` → "Now 99 problems"  (string interpolation)
Exercise: Try these out in a REPL.

Many of the operators you see above are familiar. To use operators correctly, you need to understand precedence, associativity, fixity, and arity. JavaScript has a ton of operators.

For reference (and to get the big picture), here’s the full list of JavaScript operators, from highest to lowest precedence. The list may appear overwhelming at first, but that is because having a lot of operators makes a language expressive, powerful, and useful. The good news is that you do not have to memorize them all, of course; however, if you see them in other folks’ code, you need to be able to look them up.

Precedence Description Associativity Structure
21 Grouping N/A ()
20 Member Access Left .
Computed Member Access Left []
new (with argument list) N/A new()
Function Call Left ()
Null-Safe Member Access Left ?.
19 new (without argument list) N/A new
18 Postfix Increment N/A ++
Postfix Decrement --
17 Logical NOT N/A !
Bitwise NOT ~
Unary Plus +
Unary Negation -
Prefix Increment ++
Prefix Decrement --
Type typeof
Void void
Delete property delete
Await await
16 Exponentiation Right **
15 Multiplication Left *
Division /
Remainder %
14 Addition Left +
Subtraction -
13 Bitwise Left Shift Left <<
Bitwise Right Shift >>
Bitwise Unsigned Right Shift >>>
12 Less Than Left <
Less Than Or Equal <=
Greater Than >
Greater Than Or Equal >=
Property Test in
Instance Test instanceof
11 Similarity Left ==
Non-Similarity !=
Equality ===
Inequality !==
10 Bitwise AND Left &
9 Bitwise XOR Left ^
8 Bitwise OR Left |
7 Logical AND Left &&
6 Logical OR Left ||
5 Nullish Coalesce Left ??
4 Conditional Right ?:
3 Assignment Right =
+=
-=
**=
*=
/=
%=
<<=
>>=
>>>=
&=
^=
|=
&&=
||=
??=
2 Yield right yield
Delegate Yield yield*
1 Sequence Left ,

Let’s practice.

Exercise: Try out the following expressions in a REPL. You don’t have to understand all the results at this time, but you might want to try guessing:
  • 73 + 8 * 6
  • true || false
  • 1,000
  • 3 ** 5
  • ~33
  • 'from' in Array

A good way to learn about the technical details of precedences, and gain some familiarity with operators, is to perform some exercises in which you determine (by hand) the structure of an expression:

CLASSWORK
Use the precedence and associativity rules from the table to form the “fully-parenthesized” equivalent of the following expressions. For example, given 9 * 7 - 8 - 2 ** 10, you would answer ((9 * 7) - 8) - (2 ** 10).
  • 1 + 2 + 3 + 4
  • 1 ** 2 ** 3 ** 4
  • 3 < 8 < 13 >= 1 - 3 >>> --p | q ^ r
  • yield typeof --x * 12 || yield 3 in x !== 5
  • 3, 5 * 2 >> new Dog / p += 8 || p.x[3]

Variables

A variable holds a value. Introduce them with let when you intend the value in the variable to change, and const when they are not supposed to. (You can also use var, but don’t.) A lot of people like to use ALL_CAPS to name variables that are abbreviation for fixed, important values.

const KILOGRAMS_PER_POUND = 0.45359237;
const METERS_PER_INCH = 0.0254;
const pounds = prompt("Enter your weight in pounds");
const inches = prompt("Enter your height in inches");
const kilos = pounds * KILOGRAMS_PER_POUND;
const meters = inches * METERS_PER_INCH;
alert("Your body mass index is " + kilos / (meters * meters));

Think of variables as boxes labeled with the variable name and containing the (current) value. Example:

bmivars.png

JavaScript has rules for what can and cannot be a variable name. See the official language reference for those rules.

An assignment expression replaces the current value of the variable. Do not confuse variable declarations with assignment expressions.

let x = 2;     // Variable declaration
let y = 5;     // Variable declaration
x = y + 3;     // Assignment. New value of x is 8. Old value lost.
Assignment overwrites the variable’s value. You cannot get the old value back.

The ability to mutate variables at any time can make programs hard to reason about. For this reason, many programmers try to avoid assignment as much as possible.

Statements

A statement performs an action. We say that expressions are evaluated but statements are executed. JavaScript has statements for:

Examples follow. We’ll describe the details of the following two statements in class:

const n = prompt("Enter a number:");
if (n < 0) {
  alert("Cool, a negative number!");
} else {
  alert("Okay, thanks.");
}
let total = 1000;
let year = 0;
while (total < 5000) {
  year = year + 1;
  total = total * 1.05;
}
console.log(`It will take you ${year} years to get there.`);

Comments

A comment is some descriptive text inside the script; it does not do anything when the script is run. Write comments that explain, at a high-level, what the code is supposed to do. ONLY write comments to clarify code that isn’t obvious. NEVER write comments that “repeat the code.” What do you make of this code?

/*
 * This script prompts the user to enter four words: a noun, then
 * a verb, then an adjective, and finally an adverb. It then alerts
 * a sentence using these four words. The template for the sentence
 * is one that would make sense if the user entered "dog" for each
 * prompt.
 */

// Prompt for the four words
const noun = prompt("Enter a noun");
const verb = prompt("Enter a verb");
const adjective = prompt("Enter an adjective");
const adverb = prompt("Enter an adverb");

// Alert the composed sentence
alert("If you " + verb + " a " + noun + " during the " +
  adjective + " days of summer, you’ll be " + adverb +
  " tired.");

The last two comments above are of questionable value. I don’t like them myself.

Here are words of wisdom from Robert C. Martin (a.k.a. Uncle Bob), taken from this book, which you should buy and read cover to cover:

Nothing can be quite so helpful as a well-placed comment. Nothing can clutter up a module more than frivolous dogmatic comments. Nothing can be quite so damaging as an old crufty comment that propagates lies and misinformation.

Comments are not like Schindler’s List. They are not “pure good.” Indeed, comments are, at best, a necessary evil. If our programming languages were expressive enough, or if we had the talent to subtly wield those languages to express our intent, we would not need comments very much—perhaps not at all.

The proper use of comments is to compensate for our failure to express ourself in code. Note that I used the word failure. I meant it. Comments are always failures. We must have them because we cannot always figure out how to express ourselves without them, but their use is not a cause for celebration.

So when you find yourself in a position where you need to write a comment, think it through and see whether there isn’t some way to turn the tables and express yourself in code. Every time you express yourself in code, you should pat yourself on the back. Every time you write a comment, you should grimace and feel the failure of your ability of expression.

That said, if you are writing code that is weird, hacky, hard to understand, or hard to figure out, do leave comments that explain “what the hell you did and why” for your future self and anyone else that has to maintain the code after you.

When you do decide that you can’t express yourself in code, and you are going to need that comment, ask yourself how you can avoid what Kelly Sutton has coined Comment Drift.

Summary

We’ve covered:

  • Values and Types
  • Expressions
  • Variables
  • Statements
  • Comments