Javascript


John Alexis Guerra Gómez


Slides:http://johnguerra.co/lectures/webDevelopment_fall2025/05_Javascript/

Class page:http://johnguerra.co/classes/webDevelopment_fall_2025/

What did we learn last time?

Readings discussion

  • questions?

How to ask a question to the staff

  1. Try yourself
  2. Google it
  3. StackOverflow it
  4. Create a reproducible bug (e.g. jsfiddle)
  5. Ask on slack (#general)

Random Questions

  • What is let and why we need it?
  • What is a linter
  • It's Javascript Object Oriented?

MDN JavaScript Guide

Adapted by Claude

Based on: MDN JavaScript Guide

What is JavaScript?

  • High-level, interpreted programming language
  • Originally created for web browsers (1995)
  • Now runs everywhere: servers, mobile, desktop
  • Dynamic, weakly typed, prototype-based
  • One of the core technologies of the web

JavaScript vs ECMAScript

  • ECMAScript is the specification
  • JavaScript is an implementation
  • ES6/ES2015 introduced major improvements
  • Annual releases (ES2016, ES2017, etc.)
  • Modern browsers support most ES6+ features

Where JavaScript Runs

Client-side

  • Web browsers
  • Mobile apps (React Native)
  • Desktop apps (Electron)

Server-side

  • Node.js
  • Serverless functions
  • APIs and microservices

Grammar and Types

Variable Declarations

var vs let vs const

// var - function scoped, hoisted
var name = "John";
var name = "Jane"; // OK

// let - block scoped
let age = 25;
// let age = 30; // Error!

// const - block scoped, immutable
const PI = 3.14159;
// PI = 3; // Error!
  • var: function scoped, can redeclare
  • let: block scoped, no redeclaration
  • const: block scoped, immutable binding
  • Always prefer const, then let

Data Types

Primitives

  • number: 42, 3.14
  • string: "Hello"
  • boolean: true/false
  • undefined: undefined
  • null: null
  • symbol: Symbol("id")
  • bigint: 123n

Objects

  • object: {}, []
  • function: function() {}
  • Everything else!

Type Coercion Examples

// Automatic type conversion
"5" + 3        // "53" (string concat)
"5" - 3        // 2 (number subtraction)
"5" == 5       // true (loose equality)
"5" === 5      // false (strict equality)

// Falsy values
if (!0) console.log("0 is falsy");
if (!"") console.log("empty string is falsy");
if (!null) console.log("null is falsy");
if (!undefined) console.log("undefined is falsy");

Functions

Function Declaration vs Expression

Declaration

// Hoisted - can call before definition
console.log(add(2, 3)); // 5

function add(a, b) {
  return a + b;
}

Expression

// Not hoisted
// console.log(subtract(5, 2)); // Error!

const subtract = function(a, b) {
  return a - b;
};

Arrow Functions

// Traditional function
const multiply = function(a, b) {
  return a * b;
};

// Arrow function - concise
const multiply2 = (a, b) => a * b;

// Arrow function - with block
const divide = (a, b) => {
  if (b === 0) throw new Error("Division by zero");
  return a / b;
};

// Single parameter (parentheses optional)
const square = x => x * x;

Scope and Closures

function createCounter() {
  let count = 0;
  
  return function() {
    count++;
    return count;
  };
}

const counter1 = createCounter();
const counter2 = createCounter();

console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1 (separate closure)

Parameters and Arguments

// Default parameters
function greet(name = "World") {
  return `Hello, ${name}!`;
}

// Rest parameters
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

// Destructuring parameters
function printUser({name, age, email}) {
  console.log(`${name} (${age}): ${email}`);
}

printUser({name: "John", age: 30, email: "john@example.com"});

Control Flow

Conditionals

if/else

const age = 18;

if (age >= 21) {
  console.log("Can drink");
} else if (age >= 18) {
  console.log("Can vote");
} else {
  console.log("Too young");
}

// Ternary operator
const status = age >= 18 ? "adult" : "minor";

switch

const day = "monday";

switch (day) {
  case "monday":
  case "tuesday":
    console.log("Weekday");
    break;
  case "saturday":
  case "sunday":
    console.log("Weekend");
    break;
  default:
    console.log("Unknown day");
}

Error Handling

function parseJSON(str) {
  try {
    const data = JSON.parse(str);
    return { success: true, data };
  } catch (error) {
    console.error("Parse error:", error.message);
    return { success: false, error: error.message };
  } finally {
    console.log("Parse attempt completed");
  }
}

// Throwing custom errors
function divide(a, b) {
  if (b === 0) {
    throw new Error("Division by zero is not allowed");
  }
  return a / b;
}

Loops and Iteration

Basic Loops

for loop

for (let i = 0; i < 5; i++) {
  console.log(i);
}

while loop

let i = 0;
while (i < 5) {
  console.log(i);
  i++;
}

do...while loop

let i = 0;
do {
  console.log(i);
  i++;
} while (i < 5);

break/continue

for (let i = 0; i < 10; i++) {
  if (i === 3) continue; // skip 3
  if (i === 7) break;    // stop at 7
  console.log(i);
}

for...in vs for...of

for...in (properties)

const obj = {a: 1, b: 2, c: 3};

for (const key in obj) {
  console.log(key, obj[key]);
}
// Output: "a" 1, "b" 2, "c" 3

const arr = ["x", "y", "z"];
for (const index in arr) {
  console.log(index); // "0", "1", "2"
}

for...of (values)

const arr = ["x", "y", "z"];

for (const value of arr) {
  console.log(value);
}
// Output: "x", "y", "z"

const str = "hello";
for (const char of str) {
  console.log(char); // "h", "e", "l", "l", "o"
}

Objects and Classes

Creating Objects

Object Literals

const person = {
  name: "John",
  age: 30,
  greet() {
    return `Hello, I'm ${this.name}`;
  }
};

console.log(person.greet()); // Hello, I'm John

Constructor Functions

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const john = new Person("John", 30);

Object.create()

const personProto = {
  greet() {
    return `Hello, I'm ${this.name}`;
  }
};

const john = Object.create(personProto);
john.name = "John";
john.age = 30;

Property Access

// Dot notation
person.name = "Jane";

// Bracket notation
person["age"] = 25;

// Dynamic properties
const prop = "email";
person[prop] = "jane@example.com";

Classes (ES6+)

class Animal {
  constructor(name, species) {
    this.name = name;
    this.species = species;
  }

  speak() {
    return `${this.name} makes a sound`;
  }

  static getKingdom() {
    return "Animalia";
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name, "Canis lupus");
    this.breed = breed;
  }

  speak() {
    return `${this.name} barks`;
  }
}

const dog = new Dog("Rex", "Golden Retriever");
console.log(dog.speak()); // Rex barks

Getters and Setters

class Circle {
  constructor(radius) {
    this._radius = radius;
  }

  get radius() {
    return this._radius;
  }

  set radius(value) {
    if (value < 0) {
      throw new Error("Radius cannot be negative");
    }
    this._radius = value;
  }

  get area() {
    return Math.PI * this._radius ** 2;
  }
}

const circle = new Circle(5);
console.log(circle.area); // 78.54

Modern JavaScript Features

Template Literals

const name = "John";
const age = 30;

// Old way
const message1 = "Hello, my name is " + name + " and I'm " + age + " years old.";

// Template literals (ES6+)
const message2 = `Hello, my name is ${name} and I'm ${age} years old.`;

// Multi-line strings
const html = `
  

${name}

Age: ${age}

`; // Tagged templates function highlight(strings, ...values) { return strings.reduce((result, string, i) => { return result + string + (values[i] ? `${values[i]}` : ''); }, ''); } const highlighted = highlight`Name: ${name}, Age: ${age}`;

Destructuring

Array Destructuring

const arr = [1, 2, 3, 4, 5];

const [first, second, ...rest] = arr;
console.log(first); // 1
console.log(rest);  // [3, 4, 5]

// Swapping variables
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1

Object Destructuring

const user = {
  name: "John",
  age: 30,
  email: "john@example.com"
};

const {name, age, email} = user;

// With aliases
const {name: userName, age: userAge} = user;

// With defaults
const {city = "Unknown"} = user;

Spread and Rest

Spread Operator

// Arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];

// Objects
const obj1 = {a: 1, b: 2};
const obj2 = {c: 3, d: 4};
const merged = {...obj1, ...obj2};

// Function calls
const numbers = [1, 2, 3];
console.log(Math.max(...numbers));

Rest Parameters

function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}

console.log(sum(1, 2, 3, 4)); // 10

// With other parameters
function greet(greeting, ...names) {
  return `${greeting} ${names.join(", ")}`;
}

console.log(greet("Hello", "John", "Jane"));

Promises and Async/Await

Promises

function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({id, name: `User ${id}`});
      } else {
        reject(new Error("Invalid ID"));
      }
    }, 1000);
  });
}

fetchUser(1)
  .then(user => console.log(user))
  .catch(error => console.error(error));

Async/Await

async function getUser(id) {
  try {
    const user = await fetchUser(id);
    console.log(user);
    return user;
  } catch (error) {
    console.error(error);
    throw error;
  }
}

// Usage
getUser(1);

// Multiple async operations
async function getMultipleUsers() {
  const users = await Promise.all([
    fetchUser(1),
    fetchUser(2),
    fetchUser(3)
  ]);
  return users;
}

Modules (ES6+)

Exporting

// math.js
export function add(a, b) {
  return a + b;
}

export const PI = 3.14159;

export default class Calculator {
  multiply(a, b) {
    return a * b;
  }
}

// Alternative export syntax
function subtract(a, b) {
  return a - b;
}
export { subtract };

Importing

// main.js
import Calculator, { add, PI } from './math.js';

const calc = new Calculator();
console.log(calc.multiply(2, 3)); // 6
console.log(add(4, 5)); // 9
console.log(PI); // 3.14159

// Import all
import * as math from './math.js';
console.log(math.add(1, 2));

// Dynamic imports
async function loadMath() {
  const math = await import('./math.js');
  return math.add(1, 2);
}

Collections and Data Structures

Arrays and Methods

const numbers = [1, 2, 3, 4, 5];

// Transformation methods
const doubled = numbers.map(n => n * 2);        // [2, 4, 6, 8, 10]
const filtered = numbers.filter(n => n > 3);    // [4, 5]
const sum = numbers.reduce((acc, n) => acc + n, 0); // 15

// Find methods
const found = numbers.find(n => n > 3);         // 4
const foundIndex = numbers.findIndex(n => n > 3); // 3

// Check methods
const hasEven = numbers.some(n => n % 2 === 0); // true
const allPositive = numbers.every(n => n > 0);  // true

// Modification methods
numbers.push(6);      // Add to end
numbers.unshift(0);   // Add to beginning
numbers.splice(2, 1); // Remove 1 element at index 2

Sets and Maps

Set

const uniqueNumbers = new Set([1, 2, 3, 3, 4]);
console.log(uniqueNumbers); // Set {1, 2, 3, 4}

uniqueNumbers.add(5);
uniqueNumbers.delete(1);
console.log(uniqueNumbers.has(2)); // true
console.log(uniqueNumbers.size);   // 4

// Convert to array
const arr = [...uniqueNumbers];

Map

const userRoles = new Map();
userRoles.set("john", "admin");
userRoles.set("jane", "user");

console.log(userRoles.get("john")); // "admin"
console.log(userRoles.has("bob"));  // false
console.log(userRoles.size);        // 2

// Iterate
for (const [user, role] of userRoles) {
  console.log(`${user}: ${role}`);
}

Practical Examples

DOM Manipulation Example

const btn = document.querySelector("#btnExample");
const output = document.querySelector("#output");
let count = 0;

btn.addEventListener("click", () => {
  count++;
  output.innerHTML = `
    

Clicked ${count} time${count !== 1 ? 's' : ''}!

`; });

Fetch API Example

// Modern way to make HTTP requests
async function fetchUsers() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const users = await response.json();
    return users;
  } catch (error) {
    console.error('Failed to fetch users:', error);
    throw error;
  }
}

// Usage
fetchUsers()
  .then(users => {
    users.forEach(user => console.log(user.name));
  })
  .catch(error => console.error(error));

Best Practices

  • Use const by default, let when reassigning
  • Prefer arrow functions for callbacks
  • Use async/await over .then() chains
  • Always handle errors (try/catch, .catch())
  • Use strict equality (===) over loose (==)
  • Keep functions small and focused
  • Use meaningful variable and function names
  • Leverage modern ES6+ features

Common Patterns

Module Pattern

const userManager = (() => {
  let users = [];
  
  return {
    addUser(user) {
      users.push(user);
    },
    getUsers() {
      return [...users];
    },
    getUserCount() {
      return users.length;
    }
  };
})();

Observer Pattern

class EventEmitter {
  constructor() {
    this.events = {};
  }
  
  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(listener => 
        listener(data)
      );
    }
  }
}

What's Next?

  • Frameworks: React, Vue, Angular, Svelte
  • Build Tools: Webpack, Vite, Parcel
  • Testing: Jest, Cypress, Playwright
  • TypeScript: Static typing for JavaScript
  • Node.js: Server-side JavaScript
  • Advanced Topics: Web Workers, PWAs, WebAssembly

Resources for Learning More

Summary

  • JavaScript is a versatile, dynamic language
  • Modern ES6+ features make code more readable and maintainable
  • Understanding fundamentals is key to using frameworks effectively
  • Practice with real projects to reinforce concepts
  • The ecosystem is vast and constantly evolving

Exercise

Build a listings page from the SF Airbnb listings for Sept 2023 JSON file.

  • Share your results

Required Readings Next session

Wat?

https://www.destroyallsoftware.com/talks/wat