Core Features

September 2018: This material is under very active development, and should all be considered the "brainstorming" phase of a rational lesson design process. We would appreciate your help: please email us, file an issue in our GitHub repository, or submit a pull request. (We would particularly appreciate descriptions of common errors and how to fix them.) Everyone whose work is incorporated will be acknowledged; please note that all contributors are required to abide by our Code of Conduct.

Questions

  • How can I execute short snippets of JavaScript interactively?
  • How can I run small JavaScript programs from the command line?
  • How can I print text in JavaScript?
  • What are JavaScript’s basic data types?
  • How can I find out what type something is?
  • How do I write loops?
  • How do I write conditionals?
  • What counts as true and false?
  • How do I format text?
  • How do I format JavaScript code?
  • How do I store lists of values?
  • How do I store values by name?
  • How do I define functions?
  • How do I divide source code into multiple files?
  • Introduce enough of JavaScript’s core features to get started
  • Experiment with these interactively in the Node REPL

Hello, World

console.log('hello, world')
hello, world
  • console is a built-in module that provides basic printing services
  • Use X.Y to get part Y of thing X
  • A string can be single-quoted or double-quoted
  • Semi-colons at the ends of statements are now (mostly) optional
  • Run this program from the command line with:
node src/core/hello.js

Basic Data Types

const aNumber = 123.45
console.log('the type of', aNumber, 'is', typeof aNumber)

const anInteger = 123
console.log('the type of', anInteger, 'is', typeof anInteger)

const aString = 'some text'
console.log('the type of', aString, 'is', typeof aString)

const otherValues = [true, undefined, null]
for (let value of otherValues) {
  console.log('the type of', value, 'is', typeof value)
}

console.log('the type of', console.log, 'is', typeof console.log)
the type of 123.45 is number
the type of 123 is number
the type of some text is string
the type of true is boolean
the type of undefined is undefined
the type of null is object
the type of function () { [native code] } is function
  • for...of loops over the values in an array
    • Note: “of” not “in”
    • The latter returns the indexes of the collection (e.g., 0, 1, 2)
    • Which has some traps
    • Note that indexing starts from 0. This may be different from the language that you’re used to
    • Indentation is optional and for readability purposes only. This may be different from the language that you’re used to
  • Use const to define a constant
  • number
    • JavaScript doesn’t have separate types for integers and floating-point
    • All numbers are 64-bit floating point
    • Accurate up to 15 digits
  • string for text
  • boolean for true and false
    • We’ll see below that other things can be truthy or falsy
  • undefined means “hasn’t been given a value”
  • null means “has a value, which is nothing”
  • Functions like console.log are things too
    • We’ll look at the implications of that in detail
  • typeof returns a string

Control Flow

  • Have already seen for loop
const values = [false, true, 0, 1, '', 'text', undefined, null, [], [2, 3]]
for (let v of values) {
  if (v) {
    console.log(`${v} of type ${typeof v} is truthy`)
  } else {
    console.log(`${v} of type ${typeof v} is falsy`)
  }
}
false of type boolean is falsy
true of type boolean is truthy
0 of type number is falsy
1 of type number is truthy
 of type string is falsy
text of type string is truthy
undefined of type undefined is falsy
null of type object is falsy
 of type object is truthy
2,3 of type object is truthy
  • An array is a comma-separated list of values inside square brackets
    • Arrays are heterogeneous, i.e., can contain values of many different types
    • Including other arrays
  • Use let to define a variable (as opposed to const)
  • if and else work as they do in other languages
  • true and false: as expected
  • Numbers: 0 is falsy, all others are truthy
  • Strings: empty string is falsy, all others are truthy
  • undefined and null are both falsy
  • But an empty array is truthy
    • Argument is that there’s something there, it just happens to be empty
    • Test Array.length instead
    • This is a property, not a method
  • Always use === and !== when testing for equality and inequality

Formatting

  • Use back quotes to create multi-line strings
  • Interpolate values in these strings using ${expression}
for (let color of ['red', 'green', 'blue']) {
  const message = `color is ${color}`
  console.log(message, `and capitalized is ${color.toUpperCase()}`)
}
color is red and capitalized is RED
color is green and capitalized is GREEN
color is blue and capitalized is BLUE

Objects

const creature = {
  'order': 'Primates',
  'family': 'Callitrichidae',
  'genus': 'Callithrix',
  'species': 'Jacchus'
}

console.log(`creature is ${creature}`)
console.log(`creature.genus is ${creature.genus}`)
for (let key in creature) {
  console.log(`creature[${key}] is ${creature[key]}`)
}
creature is [object Object]
creature.genus is Callithrix
creature[order] is Primates
creature[family] is Callitrichidae
creature[genus] is Callithrix
creature[species] is Jacchus
  • An object is a collection of key/value pairs
    • Keys do not have to be strings, but almost always are
    • Values can be anything
  • Create an object by putting key/value pairs in curly brackets
  • Type of object is always object
  • Can always get a value using object[key]
  • If the key has a simple name, can use object.key instead
    • The square bracket form can be used with variables for keys
    • The dotted notation cannot
  • Can write keys without quotes
    • In which case they are treated as strings
  • To get string representation of whole object, try JSON.stringify(object)

Functions

function limits (values) {
  if (!values.length) {
    return [undefined, undefined]
  }
  let low = values[0]
  let high = values[0]
  for (let v of values) {
    if (v < low) low = v
    if (v > high) high = v
  }
  return [low, high]
}
  • Definition is:
    • Keyword function
    • Function name
    • Possibly-empty list of parameters in parentheses
    • Body
  • Use return to explicitly return a value at any time
    • If nothing returned, result is undefined
  • Every function is a scope
    • Parameters and variables created inside function are local
const allTests = [
  [],
  [9],
  [3, 30, 300],
  ['apple', 'Grapefruit', 'banana'],
  [3, 'apple', ['sub-array']]
]
for (let test of allTests) {
  console.log(`limits of ${test} are ${limits(test)}`)
}
limits of  are ,
limits of 9 are 9,9
limits of 3,30,300 are 3,300
limits of apple,Grapefruit,banana are Grapefruit,banana
limits of 3,apple,sub-array are 3,3
  • Generally don’t write functions this way any longer
  • Instead, use fat arrow functions
    • Parameter list, =>, and body creates a function without a name
    • Assign that to a constant or variable
const limits = (values) => {
  if (!values.length) {
    return [undefined, undefined]
  }
  let low = values[0]
  let high = values[0]
  for (let v of values) {
    if (v < low) low = v
    if (v > high) high = v
  }
  return [low, high]
}
  • Same output as previous example

Stuck in the Past

  • Why not stick to function and fix behavior?
  • Would break legacy programs that rely on old behavior
  • Want to make it really easy to define little functions
  • The way a language is used shapes the evolution of its syntax

Modules

  • Want to put code in multiple files
  • Unavoidable bad news is that JavaScript has several module systems
    • Node still uses CommonJS
    • But is converting to modern ES6
    • So what we use on the command line is different from what we use in the browser
const bound = 3

const clip = (values) => {
  let result = []
  for (let v of values) {
    if (v <= bound) {
      result.push(v)
    }
  }
  return result
}

module.exports = {
  clip: clip
}
  • Notes
    • Don’t have to quote the keys of objects when they are simple names
    • bound isn’t exported
const utilities = require('./utilities')

const data = [-1, 5, 3, 0, 10]
const result = utilities.clip(data)
console.log(`clip(${data}) -> ${result}`)
clip(-1,5,3,0,10) -> 0,1,2,3
  • require returns an object
    • Keys are the names of the things the module exports
    • Values are the things (usually functions)
  • Usually assign result of require to a variable with the same name as the module
    • Then refer to contents as module.thing
  • Use a relative path starting with ./ or ../ to import local files
    • Paths that start with names are taken from installed Node libraries

Exercises

Typeof

What kind of thing is typeof? Is it an expression? A function? Something else? Note: You might notice that typeof typeof is syntactically invalid. In such circumstances, an internet search engine is your friend. Also recommended: the Mozilla Developer Network (MDN) JS reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference

Fill in the Blanks

Answer these questions about the program below: so that it produces the output shown.

  1. What does Array.push do?
  2. How does a while loop work?
  3. What does += do?
  4. What does Array.reverse do, and what does it return?
let current = 0
let table = []
while (current < 5) {
  const entry = `square of ${current} is ${current * current}`
  table.push(entry)
  current += 1
}
table.reverse()
for (let line of table) {
  console.log(line)
}
square of 4 is 16
square of 3 is 9
square of 2 is 4
square of 1 is 1
square of 0 is 0

What Is Truth?

Write a function called isTruthy that returns true for everything that JavaScript considers truthy, and false for everything it considers falsy, except empty arrays: isTruthy should return false for those.

Combining Different Types

What result would you expect from running the code below? Try running it and see whether the output matched your expectations. What are the implications of this behavior when working with real-world data?

const data1 = [3, 7, 8, 9, 1]
const data2 = [0, 3, -1, "NaN", 8]

console.log(`aggregating data1`)
var total = 0
for (let d of data1) {
    total += d
}
console.log(total)

console.log(`aggregating data2`)
total = 0
for (let d of data2) {
    total += d
}
console.log(total)

Change one of the loops above so that the code runs through the indexes of the array (data1 or data2) instead of the values. Does the output match your expectations this time?

What Does This Do?

Explain what is happening in the assignment statement that creates the constant creature.

const genus = 'Callithrix'
const species = 'Jacchus'
const creature = {genus, species}
console.log(creature)
{ genus: 'Callithrix', species: 'Jacchus' }

What Does This Code Do?

Explain what is happening in the assignment statement in this program. Use this technique to rewrite src/core/import.js so that clip can be called directly as clip(...) rather than utilities.clip(...).

const creature = {
  genus: 'Callithrix',
  species: 'Jacchus'
}
const {genus, species} = creature
console.log(`genus is ${genus}`)
console.log(`species is ${species}`)

Return to me, for my heart wants you only

const verbose_sum = (first, second) => {
    console.log(`Going to add ${first} to ${second}`)
    let total = first + second
    return total
    console.log(`Finished summing`)
}

var result = verbose_sum(3, 6)
console.log(result)

What output would you see in the console if you ran the code above?

  • Going to add ${first} to ${second}; 9
  • Going to add 3 to 6; 9; Finished summing
  • Going to add 3 to 6; 9
  • Going to add 3 to 6; 36

Key Points

  • Use console.log to print messages.
  • Use dotted notation X.Y to get part Y of object X.
  • Basic data types are Booleans, numbers, and character strings.
  • Ararys store multiple values in order.
  • The special values null and undefined mean ‘no value’ and ‘does not exist’.
  • Define constants with const and variables with let.
  • typeof returns the type of a value.
  • for (let variable of collection) {...} iterates through the values in an array.
  • if (condition) {...} else {...} conditionally executes some code.
  • false, 0, the empty string, null, and undefined are false; everything else is true.
  • Use back quotes and ${...} to interpolate values into strings.
  • An object is a collection of name/value pairs written in `{…}.
  • object[key] or object.key gets a value from an object.
  • Functions are objects that can be assigned to variables, stored in lists, etc.
  • function (...parameters...) {...body...} is the old way to define a function.
  • name = (...parameters...) => {...body...} is the new way to define a function.
  • Use return inside a function body to return a value at any point.
  • Use modules to divide code between multiple files for re-use.
  • Assign to module.exports to specify what a module exports.
  • require(...path...) imports a module.
  • Paths beginning with ‘.’ or ‘/’ are imported locally, but paths without ‘.’ or ‘/’ look in the library.