Week 01

JavaScript, The Basics

Case-sensitive Language

Every identifier in JavaScript is case-sensitive, meaning that a variable with the name Foo is different from a variable with the name foo:

let foo = "foo";
console.log(foo); // "foo"
console.log(Foo); // "Uncaught ReferenceError: Foo is not defined"

Comments

You can use // for single-line comments and /* */ for multi-line comments. Comments are not executed and only used for clarifications:

// this has no effect on the execution

/*
This is a multi-line
comment. Again, no effect on the execution
*/

Ending Statements

Although not necessary, it's a good idea to always end your statements with a ;:

let university = "ucalgary";

Dynamiclly Typed

JavaScript is not statically typed, meaning that the true type of a variable is decided in run-time and can change during the execution of the code:

let name = "Alice";
console.log(name); // "Alice"

name = 5; // this is fine
console.log(name); // 5
console.log(name + 10); // 15

Declaring Variables

The const Keyword

A constant is a "variable" that--unlike variables (declared with the let or var keywords)--cannot be overwritten. Once declared, you cannot change its value:

var college = "bow valley";
const university = "ucalgary";
college = "sait"; // allowed
university = "ubc"; // results in "Uncaught TypeError: Assignment to constant variable." error

The let Keyword

The let keyword declares a variable just like the var keyword does; except that the let keyword blocks off the scope of a variable in any {} blocks, such as for and if/else.

var university = "ubc";
if (university == "ubc") {
	var university = "ucalgary";
    console.log(university); // ucalgary
}
console.log(university); // ucalgary
var university = "ubc";
if (university == "ubc") {
	let university = "ucalgary";
    console.log(university); // ucalgary
}
console.log(university); // ubc

Template Strings

Template strings give us a better way to interpolate variables into strings by wrapping them inside ${}:

// old way
console.log(lastName + ", " + firstName + " " + middleName);

// new way
console.log(`${lastName}, ${firstName} ${middleName}`);

They also respect whitespace, making it easy to draw up HTML code.

document.body.innerHTML = `
<section>
  <header>
      <h1>The UCalgary Blog</h1>
  </header>
  <article>
      <h2>${article.title}</h2>
      ${article.body}
  </article>
  <footer>
      <p>copyright | The UCalgary Blog</p>
  </footer>
</section>
`;

Functions

There are multiple ways to create a function in JavaScript. We'll touch on different methods here.

Function Declarations

Function declarations start with the function keyword, folowed by the name of the function, the parameters it receives, an the body wrapped in curly braces {}.

function greetings(name) {
    return `Greetings, ${name}!`;
}

Once declared, you can call it using the function name and the necessary arguments:

console.log(greetings("Alice")); // Greetings, Alice!

Function Expressions

A function expression is just a function declaration assigned to a variable. As functions are first-class citizens in JavaScript, you can assign them to a variable, or even pass them around like a normal variable:

const greetings = function(name) {
    return `Greetings, ${name}!`;
}

console.log(greetings("Alice")); // Greetings, Alice!

One difference between function expressions and declarations is that you can't invoke a function before writing a function expression, but you can do so with function declarations:

console.log(greetings("Alice")); // Error: "Uncaught ReferenceError: Cannot access 'greetings' before initialization"

const greetings = function(name) {
    return `Greetings, ${name}!`;
}
console.log(greetings("Alice")); // Greetings, Alice!

function greetings(name) {
    return `Greetings, ${name}!`;
}

Default Parameters

You can specify a default value for function parameters, just as you would in a language like Python:

const greetings = function(name="Unknown Person") {
    return `Greetings, ${name}!`;
}

console.log(greetings()); // Greetings, Unknown Person!

Arrow Functions

Arrow functions are a relatively new addition to JavaScript. You can declare functions without using the functions keyword, and sometimes, without having to use the word return:

const greetings = name => `Greetings, ${name}!`;

console.log(greetings("Alice")); // Greetings, Alice!

If the function takes more than one argument, you need to use ():

const greetings = (name, lastName) => `Greetings, ${name} ${lastName}!`;

console.log(greetings("Alice", "Smith")); // Greetings, Alice Smith!

In case you want to return more than one statement, you need to wrap the function in {} and use the return keyword:

const greetings = (name, lastName) => {
  if (name === "Alice") {
    return `Greetings, ${name} ${lastName}! How was the Wonderland?`;
  }

  return `Greetings, ${name} ${lastName}!`;
}

console.log(greetings("Alice", "Smith")); // Greetings, Alice Smith! How was the Wonderland?

Objects and Arrays

Objects and Arrays are variables that can contain many values instead of just one. Objects are a container for key/value pairs. In order to access a field inside an object, we use their key:

const myObject = {
    name: "Alice",
    occupation: "Student"
};
console.log(myObject.name); // Alice
console.log(myObject.occupation); // Student

Arrays are containers for a list of values. In JavaScript, the values don't have to be of the same type. To access an element inside an array, we use the array index, starting from 0:

const frontendTech = ["JavaScript", "HTML", "CSS"];
console.log(frontendTech[1]); // HTML
console.log(frontendTech.length); // 3. length of the array

Destructuring Objects

Destructuring objects allows us to retrieve only the values we're interested in, instead of the whole object:

person = {
    name: "Alice",
    occupation: "Student"
};

const { name } = person;
console.log(name); // Alice
person = {
    name: "Alice",
    occupation: "Student",
    age: 25,
    goesTo: "University of Calgary",
    likes: "Programming"
};

const { name, goesTo, likes } = person;
console.log(`${name} goes to ${goesTo} and likes ${likes}.`); // Alice goes to University of Calgary and likes Programming.

We can use destructuring in function parameters too:

person = {
    name: "Alice",
    occupation: "Student",
    age: 25,
    goesTo: "University of Calgary",
    likes: "Programming"
};

const printProfile = ({ name, goesTo, likes }) => `${name} goes to ${goesTo} and likes ${likes}.`;
console.log(printProfile(person)); // Alice goes to University of Calgary and likes Programming.

It also works for nested types:

person = {
    name: "Alice",
    occupation: "Student",
    age: 25,
    goesTo: {
        universityName: "UCalgary",
        universityProvince: "Alberta"
    },
    likes: "Programming"
};

const printProfile = ({ name, goesTo: {universityName, universityProvince}, likes }) => `${name} goes to ${universityName} in the province of ${universityProvince} and likes ${likes}.`;
console.log(printProfile(person)); // Alice goes to UCalgary in the province of Alberta and likes Programming.

Destructuring Arrays

We can also destructure arrays based on their index:

const frontendTech = ["JavaScript", "HTML", "CSS"];
const [firstTech] = frontendTech;
console.log(firstTech); // JavaScript

// note how we ignore the first two using ','
const [,,lastTech] = frontendTech;
console.log(lastTech); // CSS

Adding Items to Arrays

We can use the push() method to append (add) new items to the end of an array:

const fruits = ["apple", "orange"]
fruits.push("banana")
console.log(fruits) // ['apple', 'orange', 'banana']

Object Enhancements

This is the opposite of destructuring. Basicaly, we structure or create new objects this way:

const name = "Alice";
const occupation = "Student";

const enhanced = { name, occupation };
console.log(enhanced.name, enhanced.occupation); // Alice, Student
console.log(enhanced);

We can also attach functions to an object:

const name = "Alice";
const occupation = "Student";
const printProfile = function () {
  return `Name is ${this.name} and occupation is ${this.occupation}`;
};

const enhanced = { name, occupation, printProfile };
console.log(enhanced.printProfile()); // Name is Alice and occupation is Studentf

Note the use of this in the printProfile function. this refers to the object that called the function; in this case: enhanced.


The Spread Operator

The spread operator (...) allows us to break down (spread) contents of an array or object.

const frontendTech = ["JavaScript", "HTML", "CSS"];
console.log(...frontendTech); // "JavaScript", "HTML", "CSS"

Let's copy the frontendTech array to a new one using the spread operator:

const frontendTech = ["JavaScript", "HTML", "CSS"];
const copy = [...frontendTech];
copy[0] = "TypeScript";

const [first] = frontendTech;
const [copyFirst] = copy;
console.log(first); // "JavaScript"
console.log(copyFirst); // "TypeScript"

const shallowCopy = frontendTech;
shallowCopy[0] = "TypeScript";
console.log(frontendTech[0], shallowCopy[0]); // "TypeScript", "TypeScript"

The above example shows how we can deep copy an array using the spread operator and change it later without impacting the main array. Otherwise (as shown in the second part of the example), we'll be doing a shallow copy (reference copy) and every change to either arrays will impact the other one as well.

Let's add a new element to an array:

const arr = ["Python", "Golang", "Java"];
const arr2 = [...arr, "JavaScript"];
console.log(arr.length, arr2.length); // 3, 4
console.log(arr); // ["Python", "Golang", "Java"]
console.log(arr2); // ["Python", "Golang", "Java", "JavaScript"] 

The spread operator also works with objects:

const person = {
    name: "Alice",
    occupation: "Student",
    age: 25,
    goesTo: {
        universityName: "UCalgary",
        universityProvince: "Alberta"
    },
    likes: "Programming",
    printLikes () { return `${this.name} likes ${this.likes}` }
};
console.log(person.printLikes()); // "Alice likes Programming"

const updatedPerson = {
    ...person,
    // keep everything as is, but replace the "likes" field
    likes: "Hiking"
};
console.log(updatedPerson.printLikes()); // "Alice likes hiking"