teaching machines

CS 347: Lecture 13 – Objects

October 8, 2020 by . Filed under fall-2020, lectures, webdev.

Dear students:

When we program, we organize. In particular, we bundle related data together. If the data is a sequence, we use an array. If the data is a collection of named properties, we use a key-value store. In C, this store is called a struct. In Java, this store is called a class. In JavaScript, this store is called an object.

Literals

Objects in JavaScript are kind of like ad hoc classes. We don’t declare the structure ahead of time. Rather we just create the structure on the fly using the object literal syntax:

const foundings = {Virginia: 1776};
const location = {latitude: 38.4341, longitude: 78.8632};
const phonebook2030 = {};

Destructuring

Suppose you have an object, and you want to extract its pieces into individual variables. This operation is called destructuring, and modern JavaScript supports it.

let card = {suit: 'hearts', rank: 7, isCursed: true};
let {suit, rank} = card;
console.log(suit, rank);

We see destructuring often in import statements, where we pull out just the parts of a library that we need.

Lookup

We have several ways to look up values given a key. We can use the dot or subscript operators.

console.log(foundings.Virginia);
console.log(foundings['Virginia']);

The dot operator only works when the key is a legal identifier. If the key contains punctuation or is the result of some other operation, we must use the subscript operator.

console.log(foundings['Washington, D.C.']);

const abbreviationToName = {VA: 'Virginia'};
console.log(foundings[abbreviationToName['VA']]);

Note that we can freely look up keys that don’t exist. The result is an undefined value rather than an exception. If you try to act on undefined, you will generate an exception. Since undefined is a false value, you can use the lookup in a conditional statement to preempt bad dereferencing.

if (foundings['Washington, D.C.']) {
  // safely operate on the value
}

There’s also the hasOwnProperty method for explicitly querying a key’s validity.

if (foundings.hasOwnProperty('Washington, D.C.')) {
  // safely operate on the value
}

Insertion

Unlike a struct or class, objects allow new key-value pairs to be inserted at runtime. In this sense, they are more like a map data structure. We can update foundings like this:

foundings.Wisconsin = 1848;
foundings['Wisconsin'] = 1848;

Notice that though foundings was declared const, this assignment executes just fine. That’s because const does not mean the value is constant. Only the identifier is fixed. The identifier foundings cannot be bound later to some other value. Let’s agree that const is a horribly misleading name. When you see the word const, read it as taken. A taken identifier can’t dance with other partners. But the underlying value is free to change and grow; it is mutable.

Iterating

Iterating through an object can be done by marching through the keys with JavaScript’s for-of loop:

for (let state of Object.keys(foundings)) {
  console.log(state, '->', foundings[state]);
}

If you plan to just use the key to look up the value, the entries method will give back a two-element array of the key-value pair:

for (let pair of Object.entries(foundings)) {
  console.log(pair[0], '->', pair[1]);
}

Even better is to combine entries with array destructuring to give meaningful names to the two pieces of data:

for (let [state, year] of Object.entries(foundings)) {
  console.log(state, '->', year);
}

JSON

The lightweight notation for JavaScript object literals was popular enough to be canonized as a universal data interchange format. This format is called JavaScript Object Notation, or JSON—which is pronounced like the name Jason. JSON is many language and many environments. Right now, you are using it for your configuration files in Visual Studio Code. We will use it soon to pass data between web servers and our JavaScript.

Let’s store a set of flashcard words in some raw JSON and loading it into a page to produce some interactive cards.

We start with this JSON modeling flashcards for some Spanish verbs:

{
  "from": "English",
  "to": "Spanish",
  "words": {
    "dormir": "to sleep",
    "amar": "to love",
    "comer": "to eat",
    "vivir": "to live",
    "dibujar": "to draw"
  }
}

JSON is not as loose as JavaScript. Keys must always be quoted. No trailing commas are allowed. At the same time, this file is not tied to JavaScript or our web page in any way. We could read this in any JSON parser in any language.

Now we make a web page to show these flashcards. Somehow we need to get the JSON into the page. We could use a form with a file input and some JavaScript with a FileReader. That’s too much work for this simple example. We’ll just use a textarea.

<!DOCTYPE html>
<html>
<head>
  <title>...</title>
  <link rel="stylesheet" href="flashcards.css">
  <script defer src="flashcards.js"></script>
</head>
<body>
  <textarea id="json-input"></textarea>
</body>
</html>

In JavaScript, we parse the JSON whenever there’s user input in the textarea.

const jsonInput = document.getElementById('json-input');

jsonInput.addEventListener('input', () => {
  const flashcards = JSON.parse(jsonInput.value);
});

That flashcards variable holds the inflated object. It’s no longer JSON at that point. JSON is a textual representation of an object. This is a real object with flesh on it.

We iterate through the words subobject and create a card:

const jsonInput = document.getElementById('json-input');

jsonInput.addEventListener('input', () => {
  const flashcards = JSON.parse(jsonInput.value);
  for (let [word, meaning] of Object.entries(flashcards.words)) {
    const card = document.createElement('div');
    card.classList.add('card');
    card.innerText = word;
    document.body.appendChild(card);
  }
});

The cards need some style.

.card {
  width: 300px;
  height: 200px;
  margin: 10px;
  background-color: orange;
  font-family: sans-serif;
  font-size: 3em;
  display: inline-flex;
  justify-content: center;
  align-items: center;
}

.card.flipped {
  background-color: lightgray;
}

When we click on a card, we want to flip it. This will swap out the word and meaning and change the background color.

const jsonInput = document.getElementById('json-input');

jsonInput.addEventListener('input', () => {
  const flashcards = JSON.parse(jsonInput.value);
  for (let [word, meaning] of Object.entries(flashcards.words)) {
    const card = document.createElement('div');
    card.classList.add('card');
    card.innerText = word;
    card.isFlipped = false;
    document.body.appendChild(card);

    card.addEventListener('click', () => {
      card.isFlipped = !card.isFlipped;
      card.innerText = card.isFlipped ? meaning : word;
      card.classList.toggle('flipped', card.isFlipped);
    });
  }
});

See you next time.

Sincerely,

P.S. It’s time for a haiku:

Math teaches numbers
CS teaches naming them
Likely num or foo