+ - 0:00:00
Notes for current slide
Notes for next slide

Ghost in the Machine

JavaScript

Garrick Aden-Buie

rstudio::conf(2020, "JavaScript for Shiny Users")

1

Questions and Follow up

  • Questions and answers to anything that came up in the first half
2
Any application that can be
written in JavaScript,
will eventually be
written in JavaScript.
Attwood's Law
https://blog.codinghorror.com/the-principle-of-least-power/
3

Why Javascript | http://bit.ly/jsm_19

4

Why Javascript | http://bit.ly/jsm_19

5

Why Javascript | http://bit.ly/jsm_19

6

๐Ÿ‘จ๐Ÿผโ€๐Ÿ’ป
repl_js()

7
8

what is scrabble?

tiles with letters, letters have points, squares have point multipliers

let myScore = 12
let yourScore = 33
if (true) {
console.log('Yay!')
}
9
  • if ... else
  • if ... else if ... else
  • comparisons < >
  • if (myScore)

Your Turn: Is it Truthy or Falsy?

repl_example("truthy-falsy")

Try a few of the options below to see which values are truthy and which are falsy.

  • true, false

  • A number, e.g. 42 or 0

  • A number in a string, e.g. "42" or "0"

  • Strings, e.g 'yes' or 'false'

  • A negative number

  • An object and a empty object

  • An array and an empty array

01:30
10

What's true in JavaScript?

False

  • false

  • 0

  • ""

  • null

  • undefined

  • NaN

11

What's true in JavaScript?

False

  • false

  • 0

  • ""

  • null

  • undefined

  • NaN

True

Everything else.

12

What is equal in JavaScript?

42 == '42'
true 
13

What is equal in JavaScript?

42 == '42'
true 
42 === '42'
false 
14

What is equal in JavaScript?

42 == '42'
true 

โœ– loose equality

42 === '42'
false 

โœ” strict equality

15

What is equal in JavaScript?

42 != '42'
false 
42 !== '42'
true 
16
let myScore = 12
let yourScore = 33
if (myScore > yourScore) {
console.log('Yay!')
} else {
console.log('bummer')
}
17
  • set up ternary operator
  • go to next slide to discuss
  • come back to wrap up into a result
  • Use string addition 'You ' + result + '!'
  • What is the answer to 1 + '1'?
  • String templates are awesome!
  • you try
const result =
myScore > yourScore ? 'win' : 'lose'
console.log(`You ${result}!`)

Brevity is the soul of ๐Ÿ˜•

condition ? true : false

18

Brevity is the soul of ๐Ÿ˜•

condition ? ๐Ÿ˜ : ๐Ÿ™

19

Brevity is the soul of ๐Ÿ˜•

condition ? ๐Ÿ˜ : ๐Ÿ™

Is condition? then ๐Ÿ˜ else ๐Ÿ™

20

Your Turn: Game Over!

repl_example("if-else-game-over")

Use if ... else or the ternary if statement to determine the outcome of the game.

Tell the player how they did.
Report the final scores and the final outcome.

Use string addition

points + ' points'`

or template strings

`${points} points`
21
let scores = {player: 12, opponent: 33}
// What's the outcome of the game for player?
if (scores.player > scores.opponent) {
result = 'won'
} else if (scores.player < scores.opponent) {
result = 'lost'
} else {
result = 'tied'
}
// Tell the player how they did
let outcome = 'You scored: ' + scores.player
outcome = outcome + '\nOpponent: ' + scores.opponent
outcome = outcome + '\n\nYou ' + result
console.log(outcome)
22
  • did you come with a solution sort of like this?
  • rewrite to declare result: let result = 'tied'
  • rewrite to use += for outcome
  • demonstrate issue with newlines in string addition
  • rewrite using template strings

Destructuring

We've used variables to structure an object

let player = 12
let opponent = 33
let scores = {player: 12, opponent: 33}
scores
{"player":12,"opponent":33} 
23

Destructuring

...is sort of the opposite.

We can destructure objects by matching variable names to key names

24

Destructuring

...is sort of the opposite.

We can destructure objects by matching variable names to key names

scores
{"player":12,"opponent":33} 
25

Destructuring

...is sort of the opposite.

We can destructure objects by matching variable names to key names

scores
let {player, opponent} = scores
console.log("player: ", player)
console.log("opponent: ", opponent)
player:  12 
opponent:  33 
26

Destructuring

const scores = {player: 12, opponent: 33}
// rewrite using destructuring
if (scores.player > scores.opponent) {
result = 'won'
} else if (scores.player < scores.opponent) {
result = 'lost'
} else {
result = 'tied'
}
// Tell the player how they did
let outcome = 'You scored: ' + scores.player
outcome = outcome + '\nOpponent: ' + scores.opponent
outcome = outcome + '\n\nYou ' + result
console.log(outcome)
27

Destructuring Arrays

const wordPoints = [10, 3, 7, 4]
let [first, second] = wordPoints
console.log({ first, second })
{"first":10,"second":3} 
28

Destructuring Arrays

const wordPoints = [10, 3, 7, 4]
let [first, second, ...others] = wordPoints
console.log({ first, second, others })
{"first":10,"second":3,"others":[7,4]} 
29

Rest and Spread: The Other Three Dots

...rest gets the rest of the things that weren't pulled out

let [first, second, ...others] = wordPoints
console.log({ first, second, others })
{"first":10,"second":3,"others":[7,4]} 
30

Rest and Spread: The Other Three Dots

...rest also works for objects

scores.result = 'You lost'
console.log(scores)
let {result, ...score} = scores
console.log('result: ', result)
console.log(score.player)
{"player":12,"opponent":33,"result":"You lost"} 
result:  You lost 
12 
31

Rest and Spread: The Other Three Dots

...spread flatens out arrays and objects

let myPoints = [3, 2, 5, 4]
let yourPoints = [7, 4, 2, 8]

What will we get if we put them together like this?

let ourPoints = [myPoints, yourPoints]
ourPoints
[[3,2,5,4],[7,4,2,8]] 
32

Rest and Spread: The Other Three Dots

...spread flatens out arrays and objects

let myPoints = [3, 2, 5, 4]
let yourPoints = [7, 4, 2, 8]

If we want a flat array, we can use ...spread

let ourPoints = [...myPoints, ...yourPoints]
ourPoints
[3,2,5,4,7,4,2,8] 
33

Rest and Spread: The Other Three Dots

...spread flatens out arrays and objects

let myPoints = [3, 2, 5, 4]
let yourPoints = [7, 4, 2, 8]

Which is also useful when building up an array

let newPoints = 9
myPoints = //________
mypoints

๐Ÿค” Where do I put the dots?

  1. ...myPoints

  2. ...myPoints, ...newPoints

  3. ...newPoints

34

Rest and Spread: The Other Three Dots

...spread flatens out arrays and objects

let myPoints = [3, 2, 5, 4]
let yourPoints = [7, 4, 2, 8]

Which is also useful when building up an array

let newPoints = 9
myPoints = [...myPoints, newPoints]
myPoints
[3,2,5,4,9] 

๐Ÿค” Where do I put the dots?

  1. ...myPoints

  2. ...myPoints, ...newPoints

  3. ...newPoints

35

Rest and Spread: The Other Three Dots

...spread flatens out arrays and objects

let myPoints = [3, 2, 5, 4]
let yourPoints = [7, 4, 2, 8]
let word = 'flyby'
let lettersPoints = {b: 3, f:4, l:1, y:4}
// whats our word score?
36
  • split into an array of letters
  • create score = 0
  • add to the score
  • mention +=
  • for ... of
  • standard for loop

Rest and Spread: The Other Three Dots

...spread flatens out arrays and objects

let myPoints = [3, 2, 5, 4]
let yourPoints = [7, 4, 2, 8]
let bonusSquare = 3
let word = 'flyby'
let lettersPoints = {b: 3, f:4, l:1, y:4}
// whats our word score if we inclue bonuses?
let letters = word.split('')
let score = 0
for (let letter of letters) {
let pts = lettersPoints[letter]
score += pts
}
console.log(`${word} is worth ${score} points!`)
37

we need to use standard for loop for this

For loops are for everyone

For...of
Arrays

for (let x of stuff) {
  // something with x
}

Classic

for (start; while?; after;) {
  //something with start vars
}

38

For loops are for everyone

For...of
Arrays

for (let x of stuff) {
  // something with x
}

Classic

for (let i=0; i < stuff.length; i++;) {
  // something with i
  // generally stuff[i]
}

39

Assignment Operators

let score = 0
for (let letter of letters) {
let pts = lettersPoints[letter]
score += pts
}
40

Assignment Operators

let score = 0
for (let letter of letters) {
let pts = lettersPoints[letter]
score += pts
}
let score = 0
score += 67
score *= 2
score -= 8
score /= 3
score
42 
41

Assignment Operators

let message = 'This'
message = message + ' is'
if (message.length < 5) {
message = message + ' helpful but'
} else {
message = message + ' tiring and'
}
message = message + ' gets annoying'
message
This is tiring and gets annoying 
42

Assignment Operators

let message = 'This'
message += ' is'
if (message.length < 5) {
message += ' helpful but'
} else {
message += ' tiring and'
}
message += ' gets annoying'
message
This is tiring and gets annoying 
43

Incrementing in Place

  x++

++x  

44

While we're at it, let's make things even faster to type!

Incrementing in Place

  x++

++x  

45

Incrementing in Place

  x++

++x  

46

Incrementing in Place

  x++

++x  

47

Incrementing in Place

  x++

++x  

48

Incrementing in Place

  x++

++x  

49

Incrementing in Place

  x++

++x  

50

Incrementing in Place

  x++

++x  

51

Incrementing in Place

  x++

++x  

52

Incrementing in Place

  x++

++x  

53

Incrementing in Place

  x++

++x  

54

Incrementing in Place

  x++

++x  

55

Count the number of letters

repl_example("count-letters")

let word = 'flyby'
let lettersPoints = {b: 3, f:4, l:1, y:4}
// how many of each letter in the word?
let letters = {}
for (let letter of word) {
}
02:30
56
let word = 'flyby'
let lettersPoints = {b: 3, f:4, l:1, y:4}
// how many of each letter in the word?
let letters = {}
for (let letter of word) {
}
// repl_example("count-letters")
57

write out answer, possibly with help from the audience

or skip to next slide

let word = 'flyby'
let lettersPoints = {b: 3, f:4, l:1, y:4}
// how many of each letter in the word?
let letters = {}
for (let letter of word) {
if (letters[letter]) {
letters[letter] += 1
} else {
letters[letter] = 1
}
}
// calculate the score by letter count
letters
58
  • Show Object.keys(letters)
  • use for...of to loop over keys
  • lookup letter count and multiply by points
  • get total!
let word = 'flyby'
let lettersPoints = {b: 3, f:4, l:1, y:4}
// how many of each letter in the word?
let letters = {}
for (let letter of word) {
if (letters[letter]) {
letters[letter] += 1
} else {
letters[letter] = 1
}
}
// calculate the score by letter count
let score = 0
for (let letter of Object.keys(lettersPoints)) {
score += letters[letter] * lettersPoints[letter]
}
Functions
59

rewrite both steps as functions:

  1. countLetters(word)

  2. tallyScore(word)

discuss scope

let word = 'syzygy'
let letters = {}
// here's the for loop we wrote before...
for (let letter of word) {
if (letters[letter]) {
letters[letter] += 1
} else {
letters[letter] = 1
}
}
letters
60

replace with .forEach

let word = 'syzygy'
let letters = {}
const addLetter = (l) => {
if (letters[l]) {
letters[l] += 1
} else {
letters[l] = 1
}
}
// here's the for loop we wrote before...
// for (let letter of word) {
// add_letter(letter)
// }
word.split('').forEach(l => addLetter(l))
letters

Your Turn: Replace the for loop

repl_example("for-loop-to-for-each")

let word = 'syzygy'
let lettersPoints = {"g":2,"s":1,"y":4,"z":10}
let score = 0
// replace this for loop with .forEach()
for (let letter of word) {
score += lettersPoints[letter]
}
score
25 

Replace the for...of loop with .forEach()
using an arrow function.

03:00
61
let word = 'syzygy'
let lettersPoints = {"g":2,"s":1,"y":4,"z":10}
let score = 0
// replace this for loop with .forEach()
word.split('').forEach((l) => score += lettersPoints[l])
score
let word = 'syzygy'
let lettersPoints = {"g":2,"s":1,"y":4,"z":10}
let score = 0
// what are the points by letter of word?
// I want an array of points e.g. [1, 2, 3]
word.split('').forEach((l) => score += lettersPoints[l])
score
62

Convert to .map() to get array of points

let word = 'syzygy'
let lettersPoints = {"g":2,"s":1,"y":4,"z":10}
let score = 0
// what are the letters in the word
console.log(word.split(''))
// what are the points by letter of word?
const getPoints = (l) => lettersPoints[l]
getPoints(word.split('')[2])
word.split('')
.map((l) => lettersPoints[l])
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

Your Turn: Mapping Word Scores

repl_example("mapping-words-1")

Write one function that takes a word and returns the point values of each letter as an array.

Then use .map() to apply this function to the array of words.

const lettersPoints = {
"a": 1, "b": 3, "c": 3, // ....
}
let words = ['freezer', 'jukebox']
03:00
85
const lettersPoints = {
"a": 1, "b": 3, "c": 3, "d": 2, "e": 1, "f": 4, "g": 2, "h": 4, "i": 1, "j": 8, "k": 5, "l": 1, "m": 3, "n": 1, "o": 1, "p": 3, "q": 10, "r": 1, "s": 1, "t": 1, "u": 1, "v": 4, "w": 4, "x": 8, "y": 4, "z": 10, " ": 0
}
let words = ['freezer', 'jukebox']
// Write one function that takes a word and returns
// the point values of each letter as an array
function word2points(word) {
return word
}
// Then use map to apply this function to the
// array of words above.
words.map(word2points)
86

Your Turn: Keep on Mapping

repl_example("mapping-words-2")

Write another function that takes one array of numbers, e.g. [1, 2, , 3], and calculates the sum of the array.

Then use .map() again to get an array of word scores.

02:00
87
const lettersPoints = {
"a": 1, "b": 3, "c": 3, "d": 2, "e": 1, "f": 4, "g": 2, "h": 4, "i": 1, "j": 8, "k": 5, "l": 1, "m": 3, "n": 1, "o": 1, "p": 3, "q": 10, "r": 1, "s": 1, "t": 1, "u": 1, "v": 4, "w": 4, "x": 8, "y": 4, "z": 10, " ": 0
}
let words = ['freezer', 'jukebox']
// Write one function that takes a word and returns
// the point values of each letter as an array
function word2points(word) {
return word
.split('')
.map((l) => lettersPoints[l])
}
// Then use map to apply this function to the
// array of words above.
words.map(word2points)
// Write another function that takes an array
// of numbers and returns the total of these values
// e.g. [1, 2, 3] => 6
function score(points) {
return points
}
words
.map(word2points)
.map(score)
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
let word = 'queue'
const tiles = [
{"letter": "a", "points": 1 },
{"letter": "b", "points": 3 },
{"letter": "c", "points": 3 },
{"letter": "e", "points": 1 },
{"letter": "q", "points": 10 },
{"letter": "u", "points": 1 }
]
// what are the tiles for our word?
103

Introduce .filter()

Your Turn: High-Roller Letters

repl_example("filter-high-points")

This time, we have an array of tiles and each tile has a .letter and .points.

How many possible points values are there in Scrabble? Use .map() to get an array of all the point values and then use for ... of loop to collect unique values into an array.

Finally, use filter to get the tiles with the 3 largest point values.

const tiles = [
{"letter": "a", "points": 1 },
{"letter": "b", "points": 3 },
{"letter": "c", "points": 3 }
// ...
]
104
const tiles = [
{"letter": "a", "points": 1 },
{"letter": "b", "points": 3 },
{"letter": "c", "points": 3 },
{"letter": "d", "points": 2 },
{"letter": "e", "points": 1 },
{"letter": "f", "points": 4 },
{"letter": "g", "points": 2 },
{"letter": "h", "points": 4 },
{"letter": "i", "points": 1 },
{"letter": "j", "points": 8 },
{"letter": "k", "points": 5 },
{"letter": "l", "points": 1 },
{"letter": "m", "points": 3 },
{"letter": "n", "points": 1 },
{"letter": "o", "points": 1 },
{"letter": "p", "points": 3 },
{"letter": "q", "points": 10 },
{"letter": "r", "points": 1 },
{"letter": "s", "points": 1 },
{"letter": "t", "points": 1 },
{"letter": "u", "points": 1 },
{"letter": "v", "points": 4 },
{"letter": "w", "points": 4 },
{"letter": "x", "points": 8 },
{"letter": "y", "points": 4 },
{"letter": "z", "points": 10 },
{"letter": " ", "points": 0 }
]
// Which letters have the highest points?
// Note that tiles is now an array of objects
let allPoints = tiles.map(tile => tile.points)
let possiblePoints = []
for (let pt of allPoints) {
if (!possiblePoints.includes(pt)) {
possiblePoints.push(pt)
}
}
possiblePoints
// Then filter tiles to have an array of just
// the top 3 largest point values
tiles.filter(tile => tile.points >= 5)
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
const lettersPoints = {
"a": 1, "b": 3, "c": 3, "d": 2, "e": 1, "f": 4, "g": 2, "h": 4, "i": 1, "j": 8, "k": 5, "l": 1, "m": 3, "n": 1, "o": 1, "p": 3, "q": 10, "r": 1, "s": 1, "t": 1, "u": 1, "v": 4, "w": 4, "x": 8, "y": 4, "z": 10, " ": 0
}
let word = 'quixotic'
word.split('')
.map(letter => lettersPoints[letter])
127
.reduce((total, pts) => total + pts)

Your Turn: Reduced to a number

repl_example("mapping-words-3")

Rewrite the internals of score() to use .reduce().

Use words.map() and the functions to calculate an array of word points. Then use score() one last time to calculate the player's total score.

// replace the .forEach() method below with .reduce()
function score(points) {
let total = 0
points.forEach(pt => total += pt)
return total
}
03:00
128
const tiles = [
{"letter":"a","points":1},{"letter":"b","points":3},{"letter":"c","points":3},{"letter":"d","points":2},{"letter":"e","points":1},{"letter":"f","points":4},{"letter":"g","points":2},{"letter":"h","points":4},{"letter":"i","points":1},{"letter":"j","points":8},{"letter":"k","points":5},{"letter":"l","points":1},{"letter":"m","points":3},{"letter":"n","points":1},{"letter":"o","points":1},{"letter":"p","points":3},{"letter":"q","points":10},{"letter":"r","points":1},{"letter":"s","points":1},{"letter":"t","points":1},{"letter":"u","points":1},{"letter":"v","points":4},{"letter":"w","points":4},{"letter":"x","points":8},{"letter":"y","points":4},{"letter":"z","points":10},{"letter":" ","points":0}
]
// Earlier we found all unique point values
// We can do this in one .reduce() instead
let allPoints = tiles.map(tile => tile.points)
let possiblePoints = []
for (let pt of allPoints) {
if (!possiblePoints.includes(pt)) {
possiblePoints.push(pt)
}
}
.reduce() โ€” More fun than sum()
129
// Earlier we found all unique point values
// We can do this in one .reduce() instead
tiles.reduce(function(possible, tile) {
if (!possible.includes(tile.points)) {
possible.push(tile.points)
console.log(possible, tile.points)
}
return possible
}, [])
How are you?
130

Okay, from here we can do a group activity...

Browser Events

Discrete Events

  • click
  • dblclick
  • contextmenu
  • keyup
  • keydown
  • change
  • input

Continuous

  • mousedown
  • mouseup
  • mouseenter
  • mouseleave
  • mousemove
  • wheel

Review event listener lifecycle

131

Event Listener Template

element.addEventListener('type', function(event) {
    // do something here ...
    console.log(event)
})

132

Event Listener Template

element.addEventListener('type', function(event) {
    // do something here ...
    console.log(event)
})

Choose the element to spy on, e.g.

const btn = document.querySelector('#easy-btn')
btn.addEventListener()
// or listen to all events in the document
document.addEventListener()
133

Event Listener Template

element.addEventListener('type', function(event) {
    // do something here ...
    console.log(event)
})

134

Event Listener Template

const btn = document.querySelector('#easy-btn')

btn.addEventListener('type', function(event) {
    // do something here ...
    console.log(event)
})

Choose the event type to listen for

bit.ly/mdn-browser-events

135

Event Listener Template

const btn = document.querySelector('#easy-btn')

btn.addEventListener('click', function(event) {
    // do something here ...
    console.log(event)
})

The event object contains information about what happened

event Description
.target where the event happened
.currentTarget which element is listening
.x, .y x, y coordinates
others depending on event type
136

Event Listener Template

const btn = document.querySelector('#easy-btn')

btn.addEventListener('click', function(event) {
    // do something here ...
    console.log(event)
})

137

๐Ÿคน

repl_example("browser-event-types")

repl_example("event-bubbling")
repl_example("animation-by-transition")

138

Pair Programming: R Look-A-Likes

We take for granted the built in functions we get in R.

I've created a few exercises recreating iconic R functions.

repl_example("r-in-js")

Find a partner for pair programming.

One person chooses an exercise and is the driver and types code in their ๐Ÿ’ป.

The other person is the navigator (or back-seat driver) ๐Ÿš—.

Change roles after each exercise.

05:00
140

Questions and Follow up

  • Questions and answers to anything that came up in the first half
2
Paused

Help

Keyboard shortcuts

โ†‘, โ†, Pg Up, k Go to previous slide
โ†“, โ†’, Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
oTile View: Overview of Slides
Esc Back to slideshow