Web Dev Student Cheatsheet & Reference

Basic Keyboard Shortcuts

Shortcut( Unsorted )MacWindows
CopyCommand + CCtrl + C
PasteCommand + VCtrl + V
UndoCommand + UCtrl + U
RedoCommand + Shift + ZCtrl + Shift + Z
CutCommand + XCtrl + X
New FileCommand + NCtrl + N
Open FileCommand + OCtrl + O
Close FileCommand + WCtrl + W
SaveCommand + SCtrl + S
Save AsShift + Command + SShift + Ctrl + S
Select AllCommand + ACtrl + A
Zoom In/OutCommand + +/-Ctrl + +/-
Switch Between Open ApplicationsCommand + TabAlt + Tab

Browser Keyboard Shortcuts

Shortcut( Unsorted )MacWindows
Developer toolsOption + Command + IOption + Ctrl + I
JavaScript consoleOption + Command + JOption + Ctrl + J
Inspect elementsOption + Command + COption + Ctrl + C
Refresh pageCommand + RCtrl + R
Force refreshShift + Command + RCtrl + F5 or Ctrl + Click Browser Reload
Open new window in Incognito ModeCommand + Shift + NCtrl + Shift + N
Save current webpage as a bookmarkCommand + DCtrl + D
Traverse open tabsOption + Command + ←/→Option + Ctrl + ←/→
Open new tabCommand + TCtrl + T
Close tabCommand + WCtrl + W
Jump to URL address barCommand + LCtrl + L

VS Code Keyboard Shortcuts

In addition to built-in shortcuts, you can also create your own shortcuts or override the built-in shortcuts by opening up the command palette and searching for and editing the keybindings.json file in VS Code.

Shortcut( Unsorted )MacWindows
Add Selection to Next MatchCommand + DCtrl + D
Cut Line or SelectionCommand + XCtrl + X
Delete All Characters to the Left of the CursorCommand + Delete
Delete LineCommand + Shift + KCtrl + Shift + K
Delete by Word to the Left of CursorOption + DeleteCtrl + Backspace
Expand/Shrink Current SelectionControl + Shift + Command + ←/→Ctrl + Shift + Command + ←/→
Expand/Shrink Current Selection by WordShift + Alt + ←/→Shift + Alt + ←/→
Find in ProjectShift + Command + FShift + Ctrl + F
Find & Replace in Current FileOption + Command + FAlt + Ctrl + F
Format Currently Selected CodeCommand + K Command + FCtrl + K Ctrl + F
Format Whole FileShift + Option + FShift + Alt + F
Delete All Characters After the CursorControl + KCtrl + K
Delete by Character to the right of the cursorControl + D or Fn + DeleteDelete
Delete by Word to the Right of CursorFn + Option + DeleteFn + Alt + Backspace
Go to FileCommand + PCtrl + P
Go to LineControl + GCtrl + G
Insert Line AboveCommand + Shift + EnterCtrl + Shift + Enter
Insert Line BelowCommand + EnterCtrl + Enter
Indent/Outdent Line or SelectionCommand + ] / [Ctrl + ] / [
Jump to Matching BracketCommand + Shift + \Ctrl + Shift + \
Move Cursor to End of LineCommand + →Windows Key + →
Move Cursor to Start of LineCommand + ←Windows Key + ←
Move Cursor to End of Next WordOption + →Alt + →
Move Cursor to Start of Prior WordOption + ←Alt + ←
Move Line Up/DownOption + ↑/↓Alt + ↑/↓
Multi-Cursor EditingOption + ClickAlt + Click
Open Command PaletteShift + Command + PShift + Crtl + P
Open Keyboard Shortcuts MenuCommand + K Command + SCtrl + K Ctrl + S
Open User SettingsCommand + ,Crtl + ,
Select All Occurrences of Current Selection in Current FileShift + Command + LShift + Ctrl + L
Select Entire Line / Expand Current Line Selection DownCommand + LCtrl + L
Toggle Code CommentCommand + ?Ctrl + ?
Toggle TerminalControl + ~Ctrl + ~
Toggle Word Wrap in Current FileOption + GAlt + G
Traverse Left/Right Through Open Files/Editor TabsOption + Command + ←/→Ctrl + Page Up / Page Down

VS Code Intellisense

VS Code IntelliSense provides intelligent code completions based on language semantics and an analysis of your source code. If a language service knows possible completions, the IntelliSense suggestions will pop up as you type. If you continue typing characters, the list of members (variables, methods, etc.) is filtered to only include members containing your typed characters. VS Code IntelliSense is provided for JavaScript, TypeScript, JSON, HTML, CSS, SCSS, and Less out of the box. VS Code supports word based completions for any programming language but can also be configured to have richer IntelliSense by installing a language extension. When using Intellisense, pressing Tab or Enter will insert the selected member. You can trigger IntelliSense in any editor window by typing Control + Space.

Snippets

Snippets are predefined and customizable templates that allow you to generate repeating code patterns by only entering a short and unique series of text. There are predefined snippets that come with VS Code but you can also create and customize your own snippets or use popular snippet libraries that have been created for the VS Code marketplace.

Emmet

Emmet provides you with many helpful shortcuts and patterns for generating groups of HTML and CSS code dynamically on the fly. For example, if you want to create a nav bar with a list of 5 links you can type "nav>ul>li*5>a" followed by the Tab key. Modern versions of VSCode include Emmet out-of-the-box.

Themes

There are many ways to customize your code editor with different preset syntax highlighting themes, or you can customize your own.

Bookmarks

Helps you to navigate in your code, moving between important positions easily and quickly.

Better Comments

Helps you create more human-friendly comments in your code. Allows you to categorize and style your comments differently according to the category (e.g. TODOs, questions, important alerts, etc.).

Bracket Pair Colorizer

Matching brackets have the same color, which makes it easier to determine where you are in your code.

Code Spell Checker

Helps prevent bugs by identifying when you've misspelled words and it even works with camelCased words. It comes with built-in dictionary sets and you can also add your own custom words.

Colorize

Colorizer is great for situations when you're using abstract values for CSS colors instead the actual name of the color. For instance, instead of using color names it's common to use values in units such as RGB, HEX, HSL, etc, but unless you're a CSS color guru it's not going to be immediately clear what colors these values correspond to. Colorizer helps you instantly visualize what colors these values corresponding by highlighting the value background in your code.

CSS Peek

CSS Peek allows you to view and make edits to your CSS without having to actually open your CSS files and find the corresponding rule.

ESLint

ESLint is a popular open source JavaScript linting utility. Code linting is a type of static analysis that is frequently used to find problematic code or code that doesn't adhere to certain style guidelines.

Live Server

Live Server can launch a local development server with a live reload feature so that when you save your project any changes you've made will be reflected in the browser without you having to manually refresh it.

Material Icon Theme

Material Design (a digital design system developed by Google) themed icons that will add some flair to your VSCode folder and file icons.

Prettier

Prettier is an opinionated code formatter that saves you time and energy by automatically removing original user code styling and ensuring that outputted code conforms to a consistent style.

Settings Sync

When you get to the point where you've added some VSCode extensions and have customized your VSCode experience you may want to use this extension to synchronize your VSCode settings when you work on multiple devices (e.g. one at home and one at the office). It syncs things like extenstions, settings, keybindings, and snippets.

TODO Highlight

A common convention for setting reminders in a codebase is to leave comments prefixed with "TODO:" or "FIXME:". This extension will highlight these comments to make them easier to identify and distinguish from normal code comments.

Quokka.js

This extension is a rapid prototyping playground in your editor that allows you see the result of your programs being run right next to your code, as you type.

Command Line Interface

This is a list of some of the more commonly used commands but I haven't included examples or any of the available options/arguments/flags that you can add to fine-tune the settings and details of how the commands work.

cd

Change the current directory (i.e. folder) you're in.

cat

Add text to files. View contents of files. Create new files.

clear (or Control + L)

Clears the command line window of previous commands and responses. The history is typically still preserved and can be viewed by scrolling up with the arrow key.

help

Displays a list of all available commands.

ls

Displays a list of all files in the current working directory and allows you to view information about each of the files.

man

Displays a user manual for a command.

mkdir

Creates a new directory.

mv

Move and/or rename files.

ps

Displays a list of the currently running processes (e.g. instance of a computer program that is currently running) and information associated with those processes like Process ID (PID), which can be helpful when you'd like to force-close unresponsive programs, particularly command line programs with no GUI.

pwd

Displays the name and path of the current working directory that you're in.

rm

Deletes files and directories.

touch

Creates a new file.

Alt/Option + ←/→

Moves the cursor to the start/end of the previous/next word.

Control + E

Moves the cursor to the end of the line.

Control + C

Abort running command.

Control + A

Moves the cursor to the start of the line.

Control + K

Deletes all characters after the cursor.

Control + U

Deletes all characters before the cursor.

Git Commands

This is a list of some of the more commonly used commands and definitions but I haven't included examples or any of the available options/arguments/flags that you can add to fine-tune the settings and details of how the commands work.

git add

Places files into a status that allows changes to be committed. This status is often referred to as the "index" or the "staging area". Also allows you to start tracking new files introduced to your Git repository.

git branch

Switch branches, view prior commits, restore Working Directory files.

git checkout

Switch branches, view prior commits, restore Working Directory files.

git clone

Creates a copy of a repository into a new local directory, starts tracking remote branches and creates and checks out a new branch that is linked to the cloned repository.

git commit

Record changes in the staging area to the repository. Before you push your saved changes to a remote repository you must commit the changes.

git diff

Displays differences between two inputs, such as different iterations of a file, commits, branches, working directory, etc. It's most commonly used to view changes made to a file since the last commit.

git fetch

Download content from another repository, allowing you to view changes between your local repository and a remote repository since your last pull. Does not merge remote changes into your local repository (see git pull).

git init

Creates an empty, local Git repository that is ready to start tracking your project files.

git log

Displays a log of commits made. By default it includes the commit ID, message, author and date.

git merge

Combine independent branches of development.

git pull

Fetches content from a remote repository and merges it with another repository or local branch.

git push

Save local repository content that has been committed to a remote repository.

git remote

Create, view, and delete connections to other repositories.

git reset

A versatile and potentially dangerous command that is often used to undo changes introduced by a given commit by moving the tip of a branch to a prior commit or removing changed files from the Staging Area. You can use the command with different options that affect changes in the Working Directory and Staging Area differently, but all options can permanently remove changes you've made in some form or another so use reset with caution. It is generally advisable that you do not use reset to undo changes that have already been pushed to a public repository since other contributors could've already started working off those changes, which can later cause conflicts and confusion.

git revert

Undo the changes introduced by a specific commit and creates a new commit based on those reverted changes and keeps the project history intact. Since revert keeps the history you typically want to use it when undoing commits that have been pushed to a public repository.

git rm

Remove files from a Git repository (either from the Staging Area or the Staging Area and the Working Directory).

git stash

Take the dirty state of your Working Directory — i.e. your modified tracked files and staged changes — and temporarily store it on a stack of unfinished changes that you can reapply at any time (even on a different branch). This allows you to do things like work on another branch or pull remote changes without committing the unfinished changes.

git status

Displays the status of the current git directory, including information like the files that are being tracked, changes not staged for commit and changes to be committed.

Git/Github Definitions

Branch

Branches represent independent lines of development, each with it\\'s own working directory, staging area and project history.

Clone

A local copy of a remote repository. You often clone a repository when you want to contribute to an group/organizational project. Note that cloning is similar to forking a repository but has important differences (see section links).

Commit

A snapshot of a project at a particular point in time. One of the reasons Git is so powerful is because it allows you to easily preserve, view and edit a project's history of changes.

Detached HEAD

When a specific commit is checked out instead of a branch. When you make changes and commit them in this state, these changes do NOT belong to any branch.

Fork

A fork is a copy of a repository and it's most often used when contributing to open source software. When you fork a repository it gives you your own remote copy, which you can then clone and work on locally before pushing changes back to your remote forked copy without affecting the original project. Once you've pushed your changes to your remote copy you can submit a pull request to the original project maintainer. Note that forking is similar to cloning a repository but has important differences (see section links).

HEAD

A reference to the last commit in the currently checked-out branch.

Local Repository

Your local copy of a project repository that you can independently make changes to.

Master

The default development branch. Whenever you create a git repository, Git creates a branch called "master" and checks it out for you.

Merge

A merge occurs when you combine multiple sequences of Git histories. Most frequently used to combine two branches or combining remote changes into your local repository.

Origin

An alias used for a remote repository's url. Typically it refers to the central repository that you are pushing and pulling from. It is most commonly named "origin" because that's what Git names it by default when you clone a remote repo for the first time, but you can also rename it.

Pull Request (PR)

Pull requests allow you to tell other project collaborators about changes you've pushed to a branch in a repository on GitHub. Once a pull request is opened you can discuss and review the potential changes with collaborators and add follow-up commits before your changes are merged into the base branch.

Remote Repository

A version of your project that is hosted on the Internet or network somewhere, as opposed to your local working copy. Typically everybody involved on a project works from (git pull) and saves to (git push) a remote repository.

Staging Area / Index

A local cache where changes to your Git-tracked files are stored before they can be committed. Changes are recorded to the staging area with the git add command. You can determine if a file is in the Staging Area by running the git status command.

Untracked Files

Any files in your working directory that Git is aware of but have not been committed or staged for commit.

Working Directory / Working Tree

The checked-out state of a project's files and folders on your local machine. You must tell Git to track changes to files by running the git add command. You can view the status of the Working Directory by running the git status command.

JavaScript Functions

Functions are reusable collections of code that allow programmers to limit repetitive code, group related functionality, and build-up the overall functionality of a larger program based on smaller and more managable units of functionality. A function must be "called" or "invoked" by code in order to have it's functionality activated. Functions in JavaScript can accept any number of inputs (referred to as parameters or arguments) from the invoking code (although usually you should try to keep inputs to a minimum) and then return some kind of useful output to the code that invoked it. If a function doesn't explicitly return an output to the invoking code it will just return undefined. JavaScript functions are "first-class", which means that they generally have the same abilities as other built-in types of code in that they can be stored in variables, stored in data structures, passed to other functions, returned from other functions, and have properties attached to them. Technically, functions in JavaScript are objects, or to be more specific, they're a sub-type of objects because unlike normal objects, functions are callable. If you use the typeof operator on a function it will return function instead of object. The sections below show some of the many ways functions can be declared and used.

function declaration/statement

A commonly used form of writing a function with a name.

1function doSomething() {
2 return 'Something!';
3}

anonymous function

A function without a name. Often used as callbacks or assigned to a variable in function expressions.

1// Example of an anonymous function being stored
2// in a variable. This structure is often referred to
3// as a "function expression":
4const doSomething = function() {
5 return 'Something!';
6};
7
8// Example of an anonymous function being used
9// as a callback function that gets called after
10// 5 seconds:
11setTimeout(function() {
12 console.log('5 seconds are up!');
13}, 5000);

function expression

Since functions are "first-class" and can be treated like other types of values in JavaScript, you can assign a function to a variable that can then be used to call the function.

1const doSomething = function() {
2 return 'Something!';
3};
4
5doSomething(); // Something!

named function expression

Similar to a function expression but with a name instead of just an anonymous function. You probably won't use this form often but it can be helpful because it allows function expressions to be identified in stack-trace errors and for expressions to be called from within themselves when working with recursion.

1const doSomething = function something() {
2 return 'Something!';
3};

ES6 arrow function

Introduced with ECMAScript 6, arrow functions are a modern and concise way of writing functions with lexical scope, which basically means that when you use the this keyword it's more intuitive when determining what object this refers to.

1const doSomethingNoParams = () => {
2 return 'Something!';
3};
4
5// with only 1 parameter the parens are optional:
6const doSomethingOneParam = thing => {
7 return `Do this: ${thing}!`;
8};
9
10// with one line and implicit return you don't need curly brackets:
11const doSomethingReturnImplicitly = () => 'Something!';

function as object method

When an object property is set to a function it is often referred to as a "method".

1const person = {
2 name: 'Jason',
3 introduce: function() {
4 return `Hello! My name is ${this.name}`;
5 }
6};

ES6 shorthand-function as object method

With ECMAScript 6 syntax you can omit the word "function" and place the parens directly after the function name.

1const person = {
2 name: 'Jason',
3 introduce() {
4 return `Hello! My name is ${this.name}`;
5 }
6};

constructor function

Pre-ECMAScript 6 way to use a function that acts as a "blueprint" for creating objects. The convention is that the name of the function should start with an upper-case letter so that it's easily reconizable as a constructor. The function parameters will often be named the same thing as the object properties inside the function but they don't have to be, as the example below shows.

1function Person(n, a) {
2 this.name = n;
3 this.age = a;
4 // adding a function inside the constructor will
5 // add the function to each object created by this
6 // constructor, which is often unnecessary since
7 // it takes up more memory
8 this.sayHelloV1 = function() {
9 return `Hello! My name is ${this.name} and I'm ${this.age} years young!`;
10 };
11}
12
13// by adding a function to Person.prototype you
14// allow all Person objects access to it
15Person.prototype.sayHelloV2 = function() {
16 return `Hello! My name is ${this.name} and I'm ${this.age} years young!`;
17};
18
19// create new Person object
20const joeBlow = new Person('Joe Blow', 30);

functions as methods of ES6 class

1class Person {
2 // built-in constructor function that comes with class
3 constructor(n, a) {
4 this.name = n;
5 this.age = a;
6 }
7
8 // user-defined method that gets added to Person.prototype
9 // so all Person objects can access it:
10 sayHello() {
11 return `Hello! My name is ${this.name} and I'm ${this.age} years young!`;
12 }
13
14 // Adding the "static" keyword before a function name defines a
15 // static method for a class. Static methods are called without
16 // instantiating their class and cannot be called through a class instance.
17 static staticMethodHere() {
18 return "I'm a static method that can only be called from the Person class itself!";
19 }
20}
21
22// create new Person object:
23const joeBlow = new Person('Joe Blow', 30);
24
25// call prototype method:
26joeBlow.sayHello();
27
28// call static method
29Person.staticMethodHere();

immediately invoked function expression (IIFE - pronounced “iffy”)

An anonymous function that is executed immediately (i.e. it is self-invoking). IIFEs were used more frequently in older versions of JavaScript as a way to create modules and keep code private and out of the global scope, but there are better ways to accomplish this now so you will probably mostly see IIFEs in legacy code.

1(function() {
2 return 'Something!';
3})();
4
5// with ES6 arrow function
6(() => {
7 return 'Something!';
8})();
9
10// Note: There some other ways to write IIFEs
11// but these are probably the most common

JavaScript Loops

for

Gives you control over how the loop starts, runs, and ends. Most commonly used to loop over arrays and strings.

1// common form used to iterate through each value in an array or another iterable from start to end
2for (let i = 0; i < iterable.length; i++) {
3 // doSomething
4}
5
6// loop from end of iterable to start
7for (let i = iterable.length - 1; i > -1; i--) {
8 // doSomething
9}
10
11// loop over every other item of a iterable starting from the third item
12for (let i = 2; i < iterable.length; i += 2) {
13 // doSomething
14}
15
16// loops can be nested
17for (let i = 0; i < iterableOne.length; i++) {
18 for (let j = 0; j < iterableTwo.length; j++) {
19 // doSomething
20 }
21}

for-of

Creates a loop iterating over iterable objects such as Array, String, array-like objects (e.g. HTMLCollection, NodeList, Map, Set, and user-defined iterables). Does not loop over normal JavaScript objects (see for-in loop, Object.keys, Object.values and Object.entries).

1const technologies = ['JS', 'HTML', 'CSS'];
2for (const val of technologies) {
3 console.log(val); // logs 'JS', 'HTML', 'CSS'
4}
5
6// If you'll be reassigning/operating on the values
7// you're looping over you can declare the variable with let instead of const
8const numbers = [10, 20, 30];
9for (let val of numbers) {
10 val += 1;
11 console.log(val); // logs 11, 21, 31
12}

for-in

Iterates over all enumerable properties of an object, including inherited properties/methods from the prototype chain (see 2nd example below). Most object properties that you'll create will be enumerable, but it's possible to set properties to be non-enumerable so that they don't show up in for-in loops.

1const myObject = { a: 1, b: 2, c: 3 };
2for (const property in myObject) {
3 console.log(`${property}: ${myObject[property]}`); // logs "a: 1", "b: 2", "c: 3"
4}
5
6// Example showing that enumerable, prototype properties are iterated
7function Person(first, last) {
8 this.firstName = first;
9 this.lastName = last;
10}
11
12Person.prototype.sayHello = function() {
13 return `Hello! My name is ${this.firstName} ${this.lastName}!`;
14};
15
16const jason = new Person('jason', 'roundtree');
17
18for (const property in jason) {
19 console.log(property); // logs 'jason', 'roundtree', 'sayHello'
20}
21
22// NOTE: To check if a property exists on an object rather
23// than being inherited from the prototype chain you can
24// use the .hasOwnProperty method
25// NOTE: prototype methods on ES6 classes are not enumerable
26// and will not show up in for-in loops

while

Continues a loop as long as the conditional is true.

1let i = 0;
2while (i < 3) {
3 console.log(i); // logs 0, 1, 2
4 i++;
5}

do-while

Similar to a while loop but executes the statement at least once, even if the condition starts out as false.

1let i = 0;
2do {
3 console.log(i); // logs 0, 1, 2
4 i++;
5} while (i < 3 && i !== 0);

forEach

Executes a callback function once for each array element. Returns undefined.

1const array = ['a', 'b', 'c'];
2 array.forEach(item => {
3 console.log(item); // logs 'a', 'b', 'c'
4 doSomethingWithItem(item);
5});

map

Creates a new array populated with the results of calling a provided callback function on every element in the calling array.

1const numbers = [1, 4, 9];
2const squareRoot = number => Math.sqrt(number);
3const roots = numbers.map(squareRoot);
4console.log(roots); // [1, 2, 3]

JavaScript Conditional Statements & Truthy vs. Falsy

Falsy values

"Falsy" values are any values or expressions that evaluate to the boolean value of false.

1// Examples of falsy values
2false
30
4-0
5null
6undefined
7NaN
8// (these are empty strings)
9""
10''

Truthy values

"Truthy" values are any values or expressions that evaluate to the boolean value of true.

1// Examples of truthy values
2true
3{}
4[]
51
6-1
7"I'm a string"
8"0"

if / else-if / else

1if (x === true) {
2 // do something...
3} else if (y === true) {
4 // or do something else...
5} else {
6 // or if none of the above is true, do this...
7}
8// NOTE: You can have many different else-if statements but if you find yourself
9// writing a lot of them you're probably better off using a switch statement.
10// NOTE: You can nest if-else statements
11
12// instead of writing out "x === true" and "y === true" like in the
13// example above, you can shorten it to this:
14if (x) {
15 // do something...
16} else if (y) {
17 // or do something else...
18} else { /* ... */ }
19
20// An implicit format of if-else statements can be used in
21// functions since when the condition is true the return
22// keyword prevents the subsequent code from running:
23if (valueIsTrue) {
24 return doSomething();
25}
26doSomethingElse();

Switch statements

Similar to if / else-if / else statements but with a different syntax that makes it easier to write and read when working with multiple conditions to check.

1switch(expressionToEvalute) {
2 case 'x':
3 // do something...
4 break;
5 case 'z':
6 // or do something else...
7 break;
8 case 'z':
9 // or do something else...
10 break;
11 default:
12 // or if none of the above is true, do this...
13}
14
15// NOTE: If a case is true, the break keyword will prevent
16// subsequent cases from being checked

Ternary statement

A concise syntax for writing conditional statements.

1conditionToCheck ? doSomethingIfTrue() : doSomethingIfFalse();
2
3// You can also break the clauses onto new lines if it
4// helps make the code more readable
5conditionToCheck
6 ? doSomethingIfTrue()
7 : doSomethingIfFalse();
8
9// NOTE: You can nest and chain ternary statements but
10// it's generally not recommended because it can make your code difficult to read and understand
11
12// Ternary statements are often helpful when rendering views with
13// libraries like React.js:
14{passwordEntered === usersPassword
15 ? <p>You've been successfully logged in!</p>
16 : <p>The password you entered does not match. Please try again.</p>
17)}

Short-circuiting logical evaluation

In JavaScript (and many other programming languages) evaluation of logical expressions occurs from left to right and logical operators like && (AND) and || (OR) will "short-circuit", which is a way of preventing unnecessary work from being done. In the context of the OR operator this means that if code on the left side of the operator is true then code on the right side of the operator will not run since in order for an OR statement to be true, only one operand (i.e. value being evaluated) needs to be true. In the context of the AND operator this means that if the code on the left side of the operator is false, the code on right side will not execute since all operands need to be true. This allows you to some cool things like concisely writing if-then logic.

1// In this example the `doSomething` function will only run
2// if the `conditionToCheck` variable evaluates to true
3let conditionToCheck = true;
4conditionToCheck && doSomething();
5// so the code above is equivalent to this:
6if (conditionToCheck === true) { doSomething(); }
7
8// Short-circuiting with the && operator is often useful when
9// rendering views with libraries like React.js:
10conditionToCheck && (
11 return <div>Render me if conditionToCheck is true</div>
12)
13
14// In this example `doSomething` will only run
15// if `conditionToCheck` is false
16let conditionToCheck = false;
17conditionToCheck || doSomething();
18// so the code above is equivalent to this:
19if (conditionToCheck === false) { doSomething(); }

JavaScript Array Methods

concat

Merges two or more arrays. This method does not change the existing array, but instead returns a new array.

every

Tests whether all elements in the array pass the test implemented by the provided function and returns a Boolean value.

filter

Creates a new array with all elements that pass the test implemented by the provided callback function.

find

Returns the value of the first element in the provided array that satisfies the provided callback function.

findIndex

Returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -1, indicating that no element passed the test.

forEach

Executes a callback function once for each array element. Returns undefined.

1const array = ['a', 'b', 'c'];
2 array.forEach(item => {
3 console.log(item); // logs 'a', 'b', 'c'
4 doSomethingWithItem(item);
5});

includes

Determines whether an array includes a certain value among its entries, returning true or false according to the results.

indexOf

Returns the first index at which a given element can be found in the array, or -1 if it is not present.

join

Creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string. If the array has only one item, then that item will be returned without using the separator.

lastIndexOf

Returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards from the last position by default.

map

Creates a new array populated with the results of calling a provided callback function on every element in the calling array.

1const numbers = [1, 4, 9];
2const squareRoot = number => Math.sqrt(number);
3const roots = numbers.map(squareRoot);
4console.log(roots); // [1, 2, 3]

pop

Removes the last element from an array and returns that element. This method changes the length of the array.

push

Adds one or more elements to the end of an array and returns the new length of the array.

reduce

Executes a reducer function (that you provide) on each element of the array, resulting in a single output value.

reverse

Reverses an array in place. The first array element becomes the last, and the last array element becomes the first.

shift

Removes the first element from an array and returns that removed element. This method changes the length of the array.

slice

Returns a shallow copy of a portion of an array into a new array selected from begin to end (end not included) where begin and end represent the index of items in that array. The original array will not be modified.

sort

Sorts the elements of an array in place and returns the sorted array. The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.

splice

Changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.

unshift

Adds one or more elements to the beginning of an array and returns the new length of the array.

Array.from

Creates a new, shallow-copied Array instance from an array-like or iterable object (e.g. HTMLCollection, NodeList, Map, Set, etc.). NOTE: If you're using ES6 JavaScript you can also use the "spread operator" ( ... ) to achieve the same thing.

Array.isArray

Determines whether the passed value is an Array.

Array.of

Creates a new Array instance from a variable number of arguments, regardless of number or type of the arguments. The difference between Array.of and the Array constructor is in the handling of integer arguments: Array.of(7) creates an array with a single element, 7, whereas new Array(7) creates an empty array with a length property of 7 (Note: this implies an array of 7 empty slots, not slots with actual undefined values).

JavaScript Objects

Object created with object literal

Using an object literal to create an object is the easiest way to define an object. A blank object consists of two curly brackets and you can either add properties (aka keys) and values to the object at the time of creation or after creation, although it's easier to assign them at the time of creation when possible.

1// create empty object
2const me = {};
3
4// Add properties and values after object creation
5me.name = 'Jason';
6me.heightInches = 75;
7me.sayHello = function() {
8 console.log(`Hi, my name is ${this.name}`);
9};
10
11// NOTE: If you add a property to an object without
12// assigning a value the value will initially be "undefined"
13
14// Add properties and values at time of object creation
15const newMe = {
16 name: 'Jason',
17 heightInches: 75,
18 sayHello: function() {
19 console.log(`Hi, my name is ${this.name}`);
20 }
21};
22
23// NOTE: functional properties such as "sayHello" are
24// typically referred to as "methods"
25
26// You can nest objects
27newMe.attributeValues = {
28 strength: 7,
29 empathy: 10,
30 persuasion: -5,
31 humor: 'Incalculable'
32}

Object created with ES6 class

1class Person {
2 constructor(n, a) {
3 this.name = n;
4 this.age = a;
5 }
6}
7// create new Person object:
8const joeBlow = new Person('Joe Blow', 30);

Object created with custom pre-ES6 constructor/class function

1function Person(first, last) {
2 this.firstName = first;
3 this.lastName = last;
4}
5// create new Person object:
6const jason = new Person('Jason', 'Roundtree');

Dot Notation vs. Bracket Notation

After object creation you can add properties and property values or retrieve property values using either dot notation or bracket notation. Dot notation is the more common route since it's less code, but bracket notation can be particularly handy and necessary if the property is being dynamically generated by your code or if the property name includes any non-allowable characters such as spaces and hyphens.

1const me = {};
2
3// DOT NOTATION:
4
5// set properties and values
6me.firstName = 'Jason';
7me.heightInches = 75;
8me.sayHello = function() {
9 console.log(`Hi, my name is ${this.name}`);
10};
11
12// get value
13console.log(me.name); // 'Jason'
14
15// BRACKET NOTATION:
16
17// set property and value dynamically
18let propFromInput = 'lastName';
19me[propFromInput] = 'Roundtree';
20// NOTE: Don't use quotes around variables with bracket notation
21
22// get value
23console.log(me['lastName']); // 'Roundtree'
24console.log(me[propFromInput]); // 'Roundtree'

Delete a property from an object

To delete a property from an object you can use the delete keyword.

1const myObject = {
2 name: 'Jason',
3 sayHello: function() {
4 console.log(`Hi, my name is ${this.name}`);
5 }
6};
7
8delete myObject.sayHello;
9console.log(myObject); // { name: 'Jason' }

Object created with Object.create method

Create a new object with an existing object as its prototype.

1const object1 = {
2 a: 'Hello'
3};
4const object2 = Object.create(object1);
5object2.b = 'World!';
6console.log(object2.a + ' ' + object2.b); // Hello World!
7console.log(object2); // { b: 'World!' }

Object prototypes

JavaScript is largely considered an object-oriented language because most things in JavaScript are objects and JavaScript gives developers the ability to follow Object Oriented Programming (OOP) techniques, similarly to other popular programming languages like Java, C++, C#, and Python. One core principle of OOP where JavaScript does things quite differently than usual is that instead of using classical inheritance to allow a descendent object (e.g. a child is a descendent of both a parent and a great-great-grandparent) to inherit it's own copies of characteristics from their ancestral objects, JavaScript links objects and delegates the descendent's "inherited" characteristics to its ancestors. This is accomplished through the use of object prototypes and prototype chains. Pretty much all objects in JavaScript have a [[Prototype]] property that references another object. When you attempt to access a given property of an object, JavaScript first starts with the object specified and if it doesn't find the property name on that object it follows the prototype chain from object to linked object until it finds the property, or otherwise returns undefined if it doesn't find it. The very top of this chain ends with Object.prototype. Object.prototype is built-in to JavaScript and has a number of methods attached to it and because of the prototype chain you can use these built-in methods from objects that you create. There's also built-in prototype methods on other object types like Arrays. For instance, theArray.prototype is what allows you to call built-in array methods like map() and filter() on your custom arrays. You can also define your own custom methods on prototypes (although you shouldn't add anything to Object.prototype), which is helpful because it allows you to access and reuse that functionality without actually copying the functionality onto the objects that use it, therefore saving memory and keeping your code cleaner.

1// This example is shown using a traditional constructor
2// function rather than the newer `class` syntax since
3// classes in JavaScript aren't true classes like in other
4// languages, but rather an abstraction that just allows
5// JavaScript's prototypal-inheritance-based system to
6// more closely resemble traditional classes.
7function Parent(name, age) {
8 this.name = name;
9 this.age = age;
10}
11
12// Sets up a custom method on the Parent prototype
13Parent.prototype.introduce = function() {
14 return `Hi! My name is ${this.name} and I'm ${this.age} years old.`;
15};
16
17// Allows Child objects to use the characteristics normally
18// passed to Parent when creating Parent objects but
19// instead use those characteristics for creating Child objects
20function Child(name, age) {
21 Parent.call(this, name, age);
22}
23
24// Sets Child's prototype to be set to Parent's prototype
25Child.prototype = Object.create(Parent.prototype);
26
27// After the step above you need to reset Child's constructor
28// function to be the constructor for creating Child objects
29// otherwise any objects created whild the Child constructor
30// will still be Parent-type objects
31Child.prototype.constructor = Child;
32
33// create Child object
34const johnny = new Child('Johnny', 5);
35
36// Even though the `introduce` method is on Parent.prototype
37// and not on Child, Child objects still have access to it.
38console.log(johnny.introduce()); // Hi! My name is Johnny and I'm 5 years old.
39
40
41// This example accomplishes the same thing as above but with
42// ES6 classes. You can see the code is much cleaner and concise
43// but JavaScript is still using more complex code "under the hood".
44class ParentES6 {
45 constructor(name, age) {
46 this.name = name;
47 this.age = age;
48 }
49 introduce() {
50 return `Hi! My name is ${this.name} and I'm ${this.age} old.`;
51 }
52}
53
54class ChildES6 extends ParentES6 {
55 constructor(name, age) {
56 super(name, age);
57 }
58}
59
60const jessica = new ChildES6('Jessica', 7);
61console.log(jessica.introduce()); // Hi! My name is Jessica and I'm 7 years old.
62

Object References

When an object is created, it exists at a specific place in memory. If you were to create two objects with identical properties, the two objects will not be equal to eachother because they exist at different places in memory. One important implication of this is that when you make changes to an object's properties after passing it to a function or assigning it to a new variable, even though it may seem like you're making changes to a copy of the original object, you're actually changing the original object.

1// Creating a new variable and setting it to `obj`
2// does not create a copy of `obj`
3const obj = {
4 a: 'Hello'
5};
6const objRef = obj;
7objRef.a = 'Goodbye';
8console.log(obj.a); // Goodbye
9
10// Since arrays are objects the same thing applies
11const arr = ['a', 'b', 'c'];
12const arrRef = arr;
13arrRef.push('d');
14console.log(arr); // ['a', 'b', 'c', 'd']

Browser/DOM Events & Event Handling

Browser/DOM events are actions that typically occur as a result of a user interacting with a website. There are many different types of events that can occur, such as a web page loading or a user clicking on element, typing into an input and submitting a form (see MDN link below for more events). By adding event listeners to the DOM and utilizing the event-based APIs built into web browsers, developers can respond to events in a programmatic way.

Event Listeners/Handlers

Code typically in the form of functions attached to the DOM that listen for specific events to occur and then react to them in some way. The terms event listeners and event handlers are often used interchangeably to refer to the same thing, although listener could specifically be referring to the part of the code that listens for an event to occur, while handler could refer to the function that is called as a result of the listener being triggered. By those specific definitions you could have multiple listeners that call the same handler function.

addEventListener

Code that attaches to a specific part of the DOM, specifies what type of event to listen for and reacts to that event. The last parameter, referred to as useCapture, is a boolean value that controls if the given event can be triggered during the Event Capturing phase and it's default value is false (this last parameter can also be an object that implements other options that are infrequently used and beyond the scope of this rudimentary description).

1const myForm = document.querySelector('form');
2myForm.addEventListener('submit', function(event) {
3 event.preventDefault();
4 submitDataToServer();
5});
6
7// Or the function can be declared outside of the listener
8function handleFormSubmission(event) {
9 event.preventDefault();
10 submitDataToServer();
11}
12myForm.addEventListener('submit', handleFormSubmission);
13
14// You may also add event listeners using the following methods,
15// but `addEventListener` is the more modern and flexible approach:
16
17// This is known as a traditional DOM event handler
18myButton.onclick = function () {
19 doSomething();
20};
21
22// If you call a named function you don't include parentheses:
23myButton.onclick = doSomething;
24
25// You can add inline event handlers directly
26// to HTML elements as attributes, assuming the
27// handler function being called is in your JavaScript
28// <button onclick=doSomething()>Click Me!</button>

removeEventListener

Remove a previously declared event listener that you no longer need or want to react to. The parameters passed to the method must match the parameters used when the event listener was added.

Event object

When an event is triggered, the function that handles the event receives an Event object from the browser that contains different properties describing the event, which developers can then use to identify and appropriately respond to the event. The most commonly utilized properties of the Event object describe where in the DOM the event occurred.

1// You can name the event object anything you want but
2// it's most common to use "event" or just "e":
3function imAnEventHandler(event) { console.log(event) }
4function imAlsoAnEventHandler(e) { console.log(e) }

Event Propagation: Event Bubbling & Event Capturing

Event Propagation is a general term for the different manners in which an event travels through the DOM once the event has occurred. Events can propagate inward, from the top of the DOM tree's Window object down to the event target element that registered the event, and outward from the event target back to the Window object. The inward propagation is known as Event Capturing or the Capturing Phase and outward propagation is known as Event Bubbling or the Bubbling Phase. The Capturing Phase occurs first and the Bubbling Phase occurs last, and in between these phases is a third phase referred to as the Target Phase, which is when the propagation reaches the event target where the event occurred. Not all event types propagate in both directions.

Event.target

The target property of the Event object is itself an object containing many helpful pieces of information about the element where an event was triggered, including information like the element's attributes, content, location and it's relation to other elements in the DOM.henry

Event.currentTarget

The currentTarget property of the Event object is similar to the target property (see section above), but instead of being the element that triggered the event it's the ancestor element higher up in the DOM that the event listener is attached to. Since events propagate throughout the DOM, event listeners don't need to be on the actual element that triggered the event. This is particularly helpful because it allows us to do things like "event delegation" (see section below). NOTE: If you try to console.log currentTarget you will often see the logged value set to null in the browser console but this is due to a weird quirk of how console.log works. Typically, if your code is correct, currentTarget is actually set to the element where the event listener exists and if you want to verify that you can first set currentTarget equal to a variable and then console.log the variable.

Event Delegation

Event delegation is when you add an event listener to a parent element of a child element where the event occurred. It's particularly helpful in that it prevents you from having to add event listeners to each child element when a long list of children elements exists and it also allows you to respond to events that occur on child elements that are dynamically added to the DOM after the page initially loads.

Event.preventDefault

Events have a default behavior in the browser that you'll sometimes want to override. For instance, the default behavior for a form upon being submitted is to send form data directly from the client-side form to a back-end server and refresh the page, but with more modern websites we often want to do some other processing of the data instead of submitting directly to the server. Another common situation to use it is with anchor tag links when instead of letting the browser follow the URL value in the href attribute you can perform some other action.

DOM Targeting, Traversing & Manipulation

querySelector

Returns the first element that is a descendant of the element on which it is invoked that matches the specified group of selectors.

querySelectorAll

Returns a static (not live) NodeList representing a list of elements matching the specified group of selectors which are descendants of the element on which the method was called.

getElementsByTagName

Returns a live HTMLCollection of elements with the given tag name. All descendants of the specified element are searched, but not the element itself. The returned list is live, which means it updates itself with the DOM tree automatically.

getElementsByClassName

Returns a live HTMLCollection which contains every descendant element that has the specified class name or names.

getElementById

Returns the element whose id property matches the specified string.

firstChild

Read-only property that returns a node's first child in the tree, or null if the node has no children.

lastChild

Read-only property that returns the last child of a node. If its parent is an element, then the child is generally an element node, a text node, or a comment node. It returns null if there are no child elements.

firstElementChild

Read-only property returns the object's first child element, or null if there are no child elements.

lastElementChild

Read-only property returns the object's last child element or null if there are no child elements.

childNodes

Read-only property that returns a live NodeList of child nodes of the given element. The first child node is assigned index 0.

children

Read-only property that returns a live HTMLCollection, which contains all of the child elements of the node upon which it was called.

parentNode

Read-only property that returns the parent of the specified node in the DOM tree.

parentElement

Read-only property returns a DOM node's parent Element, or null if the node either has no parent, or its parent isn't a DOM Element.

previousSibling

Read-only property that returns the node immediately preceding the specified one in its parent's childNodes list, or null if the specified node is the first in that list.

nextSibling

Read-only property that returns the node immediately following the specified one in their parent's childNodes, or returns null if the specified node is the last child in the parent element.

closest

Starting with the target element itself it traverses parents (heading toward the document root) of the element until it finds a node that matches the provided selectorString. Will return itself or the matching ancestor. If no such element exists, it returns null.

append

Inserts a set of Node objects or DOMString objects after the last child of the ParentNode.

prepend

Inserts a set of Node objects or DOMString objects before the first child of the ParentNode.

appendChild

Adds a node to the end of the list of children of a specified parent node. If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position (there is no requirement to remove the node from its parent node before appending it to some other node).

remove

Removes the object from the tree it belongs to.

removeChild

Removes a child node from the DOM and returns the removed node.

replaceChild

Replaces a child node within the given (parent) node.

insertAdjacentElement

Inserts an element node at a given position relative to the element it is invoked upon.

insertAdjacentText

Inserts a given text node at a given position relative to the element it is invoked upon.

insertAdjacentHTML

Parses the specified text as HTML and inserts the resulting nodes into the DOM tree at a specified position. It does not reparse the element it is being used on, and thus it does not corrupt the existing elements inside that element. This avoids the extra step of serialization, making it much faster than direct innerHTML manipulation.

insertBefore

Inserts a node before a reference node as a child of a specified parent node. If the given node already exists in the document, insertBefore() moves it from its current position to the new position (i.e. it will automatically be removed from its existing parent before appending it to the specified new parent).

innerHTML

Gets or sets the HTML markup contained within the element.

textContent

Gets or sets the text content of the node and its descendants.

innerText

Gets or sets the text content of the specified node, and all its descendants. As a getter, it approximates the text the user would get if they highlighted the contents of the element with the cursor and then copied it to the clipboard.

createElement

Creates the HTML element specified by tagName.

createTextNode

Creates a new Text node. This method can be used to escape HTML characters.

setAttribute

Sets the value of an attribute on the specified element. If the attribute already exists, the value is updated; otherwise a new attribute is added with the specified name and value.

getAttribute

Returns the value of a specified attribute on the element. If the given attribute does not exist, the value returned will either be null or an empty string.

className

Gets and sets the value of the class attribute of the specified element. If there are multiple classes on the element then the returned value will be a string of space-separated class names.

classList

Read-only property that returns a DOMTokenList, which is a live collection of the class attributes of the element (e.g using classList on an element with class="one two three" will return a list similar to the following format: { 0: 'one', 1: 'two', 2: 'three' }). This can then be used to manipulate the class list using built-in methods that are inherited from the DOMTokenList object (see link below for the available methods). Using classList is a convenient alternative to accessing an element's list of classes as a space-delimited string via element.className.

contains

Returns a Boolean value indicating whether a node is a descendant of a given node, i.e. the node itself, one of its direct children (childNodes), one of the children's direct children, and so on.

nodeName

Read-only property that returns the name of the current node as a string.

tagName

Read-only property that returns the tag name of the element on which it's called. For example, if the element is an its tagName property is IMG.

nodeType

Read-only property that returns an integer that identifies what type of node something is. It distinguishes different kind of nodes from each other, such as elements, text and comments.

nodeValue

Returns or sets the value of the a node.

JavaScript Scope and Closures

Global Scope

Global scope is when you have a variable or function that exists at the top level of your program (e.g. the window in the browser) so it can be accessed from anywhere in your program, which may seem powerful but it’s usually undesirable because it can cause clashes with other global uses of the same name (e.g. separate variables in a completely different areas of your app, variables declared and used in external libraries that you’re using in your app) and make your program more difficult to reason about.

Function Scope

Function scope means that variables and functions declared inside of a function can only be accessed by other code inside of that function and not by any code that exists outside that function.

1function greetings() {
2 var a = 'hello';
3 console.log('function scoped var: ', a);
4 function sayGreeting() {
5 console.log('function scoped function: ', `${a}, friendo!`);
6 }
7 sayGreeting(); // function scoped function: hello, friendo!
8}
9greetings(); // function scope: hello
10console.log(a); // ReferenceError: a is not defined
11sayGreeting(); // ReferenceError: sayGreeting is not defined

Block Scope

ES6 JavaScript introduced the let and const forms of declaring variables, which give us the ability to use block scope, which means that the scope is contained to the nearest set of enclosing curly brackets ( { } ). That means that you can access block-scoped variables from anywhere within a set of brackets but not from outside of those brackets.

1const a = 'hello';
2if (a === 'hello') {
3 const a = 'goodbye';
4 console.log(a); // goodbye
5 // Note: this example shows how you can declare
6 // variables with the same name in different scopes,
7 // but keep in mind that you can't declare the same
8 // variable or function names inside of the same scope
9}
10console.log(a); // hello
11
12// Example showing that the `i` variable is confined to the corresponding
13// block of the for loop because let is block-scoped
14for (let i = 1; i <= 5; i++) {
15 console.log(i); // 1 2 3 4 5
16}
17console.log(i); // ReferenceError: i is not defined
18// Note: if you used var instead of let in the example above
19// then the variable `i` would be scoped to outside of the for loop

Lexical Scope

JavaScript manages scope based on the principle of lexical scope, which means that scope is statically set when you write your code (or to be more accurate, when the code is compiled just before being executed, as opposed to potentially changing dynamically when your code is executed) and it determines how outer scopes are accessed from within nested scopes. Variables that are used inside of nested functions have access to variables of the same name that are declared in any outer functions and scopes. If you use a variable in a nested function but the variable isn't declared inside of that function then JavaScript will check in any parent functions to determine where it was declared, ultimately stopping at the first place where it was declared or all the way up to the global scope otherwise. This means that if the same variable name is declared more than once at different levels of lexical scope, then any declarations in lower-level functions will block or shadow the outer variable declarations from being accessed.

1let a = 'hello';
2let b = 'konnichiwa';
3
4function outer() {
5 a = 'hola';
6 console.log(a); // hola
7
8 function inner() {
9 // Note: `a` hasn’t been reassigned to ‘bonjour’ yet
10 console.log(a); // hola
11 a = 'bonjour';
12 console.log(a); // bonjour
13
14 function furtherInner() {
15 console.log(a); // bonjour
16 console.log(b); // konnichiwa
17 }
18 furtherInner();
19 }
20 inner();
21}
22
23outer();
24
25
26// Example of arrow functions lexically binding to
27// the object context (assume this code is at the top-level
28// of a javascript file):
29const person = {
30 name: ‘Jason’,
31
32 // this function is bound to `obj`:
33 greetings: function() {
34 console.log(this.a); // hello
35
36 // when you have a normal function that is nested inside
37 // of a method, the nested function will not be directly
38 // associated with the object it’s inside of, so this
39 // function is bound to the global object (window in browsers)
40 // where the `a` variable doesn’t exist:
41 setTimeout(function() {
42 console.log(this.a); // undefined
43 }, 1000);
44
45 // when you use an arrow function inside of a nested function
46 // then the `this` keyword is lexically bound to the object it’s
47 // inside of:
48 setTimeout(() => {
49 console.log(this.a); // hello
50 }, 2000);
51 }
52};

Hoisting

Hoisting is a term that describes how JavaScript parses a program and associates variables and functions with the top part of the scope in which they exist, before executing the program. This can allow you to do some unusual things like call functions before they are declared. Hoisting is usually something that you don’t need to consciously think about but it can manifest itself in some funky behavior in certain circumstances so it’s good to be aware of it. You can avoid weird issues caused by hoisting by calling functions and using variables lower in the program than the lines where they are defined and assigned values. Hoisting behaves slightly differently on different forms of functions and with the type of keyword you use when declaring variables.

1// variables declared with `var` are hoisted and initialized
2// with a value of `undefined`.
3console.log(greeting1); // undefined
4var greeting1;
5
6console.log(greeting2); // undefined
7var greeting2 = 'Hello world!';
8
9// variables declared with `const` and `let` are hoisted but not
10// initialized, which means that you can’t use these variables
11// before they are assigned a value.
12console.log(greeting3); // ReferenceError: Cannot access 'greeting' before initialization
13let greeting3 = 'Hello world!';
14
15// Side note - variables declared with `const` must be initialized when declared
16const greeting4; // SyntaxError: Missing initializer in const declaration
17
18// function declartions are hoisted along with the function body,
19// which allows you to call a function before it’s defined (but
20// you should probably just call functions after they’re defined)
21funcDeclaration('hello world!'); // 'hello world!
22function funcDeclaration(str) {
23 console.log(str);
24}
25
26// function expressions and Class functions are similar to
27// `let` and `const` in that they’re technically hoisted but not
28// initialized so there’s no reason to try and call a function
29// expression or Class before they have been declared.
30funcExpression('hello world!'); // ReferenceError: Cannot access 'funcExpression' before initialization
31const funcExpression = function(str) {
32 console.log(str);
33};
34
35const jason = Person('Jason', 'hello world!'); // ReferenceError: Cannot access 'Person' before initialization
36class Person {
37 constructor(name, greeting) {
38 this.name = name;
39 this.greeting = greeting;
40 }
41}

Closure

The principle of closure in JavaScript is a behavior of functions that enables them to remember the value of variables within a function’s lexical scope (i.e. where the function is physically defined in the code and the area around that function that it has access to), even when that function is executed outside of its lexical scope. This allows you to keep data private and predictable. Without closure you would have to write more code to explicitly pass variables around to different parts of your program.

1// In this example the `powerOf` function closes over the parameters
2// `base` and `exponent`. Each time the `baseOf` function is called
3// it creates a new context where the value of the `base` parameter
4// is newly created to whatever number is passed in as the argument.
5function baseOf(base) {
6 return function powerOf(exponent) {
7 return base ** exponent;
8 };
9}
10
11const base2ToThePowerOf = baseOf(2);
12const base3ToThePowerOf = baseOf(3);
13console.log(base2ToThePowerOf(8)); // 256
14console.log(base3ToThePowerOf(4)); // 81
15
16
17// in this example the `inner` function closes over variable `a`
18// and a reference to `a` is kept alive in the variable `b`
19// even after the `outer` function has been executed.
20function outer() {
21 let a = 0;
22 return function inner() {
23 a++;
24 console.log(a);
25 };
26}
27
28const b = outer();
29b(); // 1
30b(); // 2
31b(); // 3

"this" keyword in JavaScript

The this keyword in JavaScript is a mechanism for referring to the object that a property or function references or applies to. It is one of the more confusing aspects of JavaScript and it is often incorrectly thought of as a reference to the function this resides in or the scope of that function, and while these scenarios can sometimes be true, it’s more accurate to say that what this refers to is a product of the context in which the function is invoked/called. There are a handful of different ways of invoking a function that determine what context this refers to (see below).

Default Binding / Function Invocation

The most basic example of how to determine what this refers to is when a function is invoked by a plain, stand-alone function in the global scope.

1var name = 'Mason';
2function logName() {
3 var name = 'Jason';
4
5 // this logs `window` in browser (unless this function is
6 // running in `strict mode`, in which case `this` from
7 // within a function will refer to `undefined`)
8 console.log(this);
9
10 // this logs `Mason` because `this` refers to
11 // global scope not function scope:
12 console.log(this.name);
13
14 this.name = name;
15 // this logs ‘Jason’ because the global variable
16 // has been changed on the line above
17 // to the `name` variable declared within
18 // this function:
19 console.log(this.name);
20}
21
22logName();
23
24// this logs ‘Jason’ since the variable declared
25// on line 1 has been changed on line 11:
26console.log(name);

Implicit Binding / Method Invocation

When a function exists as a property of an object it is often referred to as a "method". When a function is invoked as a method, this, by default, will refer to the object that the function refers to (i.e. the first object to the left of the period prior to the property name).

1function sayHello() {
2 console.log(this.firstName + ' ' + this.lastName);
3}
4
5var firstName = 'Mason';
6var lastName = 'Poundtree';
7
8const jason = {
9 firstName: 'Jason',
10 lastName: 'Roundtree',
11 // this sayHello object property now refers to
12 // the `sayHello` function above:
13 sayHello: sayHello
14};
15
16jason.sayHello(); // Jason Roundtree
17window.sayHello(); // Mason Poundtree

Explicit Binding / Indirect Invocation

JavaScript functions have built-in utility methods named call, apply and bind that to allow you to explicitly use an object as the this context for a given function. call and apply operate almost identically, except that call accepts a list of comma-separated values as arguments while apply accepts an array of arguments. A form of explicit binding known as hard binding can be achieved by using the bind method. bind creates a new function with the passed-in object set as the function’s this value. This is similar to what call and apply do except bind returns a function to a variable that you call later instead of calling the bound function immediately.

1// call & apply:
2
3const plainJason = { name: 'Jason 🙂' };
4const coolJason = { name: 'Cool Jason 😎️' };
5
6function greet(greeting) {
7 return `${greeting} My name is ${this.name}.`;
8}
9
10console.log(greet.call(plainJason, 'Hello.'));
11// Hello. My name is Jason 🙂.
12console.log(greet.apply(coolJason, ['Sup, bruh?!']));
13// Sup, bruh?! My name is Cool Jason 😎️.
14
15// bind:
16
17var myLight = { brightness: 10 };
18
19var brightness = 5;
20
21function shineLight() {
22 return '💡'.repeat(this.brightness);
23}
24
25console.log(shineLight()); // 💡💡💡💡💡
26
27var bindedByMyLight = shineLight.bind(myLight);
28// above we've used `bind` to force `shineLight` to refer to
29// `myLight.brightness` instead of `window.brightness`
30console.log(bindedByMyLight()); // 💡💡💡💡💡💡💡💡💡💡
31

Binding with the "new" keyword

When you instantiate a function using new and a class/constructor function, this references the object created. Any methods from the constructor function are accessed by this via the prototype chain.

1class Person {
2 constructor(name, age) {
3 this.name = name;
4 this.age = age;
5 }
6 greet() {
7 return `
8 Hello, my name is ${ this.name } and I’m ${ this.age } years old.
9 `;
10 }
11}
12const jason = new Person(‘Jason’, 36);
13
14console.log(jason.name); // Jason
15console.log(jason.greet()); // Hello, my name is Jason and I’m 36 years old.

Lexical "this" & ES6 arrow functions

ES6 arrow functions are unique in that instead of following any of the rules above, the this keyword will “lexically” reference the containing scope from where the arrow function is defined.

1var mySurprisingObject = {
2 someProptery: 'i am property',
3 outer: function() {
4 console.log('outer this: ', this.someProptery);
5 function inner() {
6 console.log('inner this: ', this.someProptery);
7 }
8 inner();
9 }
10};
11mySurprisingObject.outer();
12// outer this: i am property
13// inner this: undefined
14
15// It’s natural and tempting to think that `this` in
16// the `inner` function will still refer to `myObject`
17// but it actually isn’t associated with the object at all,
18// so it is effectively defined on the global scope.
19// ES6 arrow functions fix this issue:
20
21var myMoreIntuitiveObject = {
22 someProptery: 'i am property',
23 outer: function() {
24 console.log('outer this: ', this.someProptery);
25 // an arrow function enforces that `this` refers to
26 // the expected object it's defined in:
27 const inner = () => {
28 console.log('inner this: ', this.someProptery);
29 };
30 inner();
31 }
32};
33myMoreIntuitiveObject.outer();
34// outer this: i am property
35// inner this: i am property
36
37// Prior to ES6, JavaScript developers devised the
38// following hack to allow nested functions to access
39// the proper `this` access by reassigning it to a new
40// variable in the outer method’s scope:
41
42var myHackedObject = {
43 someProptery: 'i am property',
44 outer: function() {
45 // Here we create a variable arbitrarily called `_this` to
46 // capture the myObject scope from the outer function so that
47 // the `inner` function can use it (it's common to see variable
48 // names like `self` or `that` used instead of `_this`)
49 var _this = this;
50 console.log('outer this: ', this.someProptery);
51 function inner() {
52 console.log('inner _this: ', _this.someProptery);
53 }
54 inner();
55 }
56};
57myHackedObject.outer();
58// outer this: i am property
59// inner _this: i am property
60
61// Note that arrow functions don’t have their own copy
62// of this so you gernally don’t want to use them for
63// object methods or prototype methods because the
64// lexical scope will refer to the global scope since
65// that’s where the object is lexically defined:
66
67// object method:
68var myObject = {
69 someProptery: 'i am property',
70 outer: () => {
71 console.log('outer this: ', this.someProptery);
72 }
73};
74
75// prototype method
76function Person(fullName) {
77 this.fullName = fullName;
78}
79Person.prototype.sayHello = () => {
80 console.log(`Hello, my name is ${this.fullName}`);
81};
82const jason = new Person('Jason');
83jason.sayHello(); // Hello, my name is undefined
84
85// Also note that you can’t use arrow functions for contructor
86// functions like this example, which will throw an error when you
87// try to use the constructor:
88const PersonBad = (name) => {
89 this.name = name;
90};
91