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})/
Details will come later, but in class we are going to classify the values above, and more into:
Number
and BigInt
)You’ve seen most of these in prior code-alongs; now is the time to look at them more deeply.
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)
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.
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:
Use the precedence and associativity rules from the table to form the “fully-parenthesized” equivalent of the following expressions. For example, given9 * 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]
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:
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.
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.`);
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.
We’ve covered: