Skip to content

Instantly share code, notes, and snippets.

@GraySmith00
Last active October 31, 2018 15:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GraySmith00/113786872df174d2a0911dd0b2e6eb72 to your computer and use it in GitHub Desktop.
Save GraySmith00/113786872df174d2a0911dd0b2e6eb72 to your computer and use it in GitHub Desktop.

Data Types

  • null - intentional absence of value (falsey)
  • undefined - variable without a value (falsey)
  • boolean - true or false
  • string - anything inside quotes
  • number - numbers can be whole numbers, negative, or decimals (different from other languages)
typeof(value) method is useful for determining what type of data a value is // null = object, [] = object

Variables

  • keyword, name, assigment operator, value
  • Rules: Can't begin with a number, camelCase, can't use reserved keywords, can't use dashes or periods

Operators

  • Assignment operators assign a value to a variable. (hint: you’ve got these down already) var color = 'magenta';
  • Arithmetic operators perform basic math. var addTwo = 2 + 2;
  • String operators combine strings. var greeting = 'Hello! ' + 'Nice to meet you.';
  • Comparison operators compare two values and return a true or false. var buy = 3 > 5; // Value of buy is false
    1. loose equal (==), different datatypes are still equal (type coersion), never use
    2. strict equal (===), only equal if value and datatype are the same
    3. greater than, less than, greater than equal to, less than equal to
  • Logical operators combines expressions and return a Boolean value of true or false. var buy = (5 > 3) && (2 < 4);
    1. and &&
    2. or ||
    3. not !

Switch Statement

  • use instead of 'if / else if' if you have more than 2 scenarios
  • syntax:
var text;
var fruits = document.getElementById("myInput").value;

switch(fruits) {
    case "Banana":
        text = "Banana is good!";
        break;
    case "Orange":
        text = "I am not a fan of orange.";
        break;
    case "Apple":
        text = "How you like them apples?";
        break;
    default:
        text = "I have never heard of that fruit...";
}

Truthy vs. Falsy

Truthy

  • 1
  • true
  • {}
  • []
  • "string"
  • everything else basically

Falsy

  • 0
  • null
  • undefined
  • ""
  • false
  • NaN

DOM Manipulation

Event Propigation (Capturing, Target, and Bubbling)

  • Event Handlers - Functions that will run when an event happens.

  • Event Propigation - The order in which different DOM elements are notified of an event

    1. Capturing Phase
    2. Target Phase
    3. Bubbling Phase
  • Event Capturing - listeners are fired from the top of the DOM tree down.

  • Event Targeting - listeners are fired on the source of the event.

  • Event Bubbling - listeners are fired from the target event up.

  • Default Event listener behavior is to fire the targeted event listener and then bubble up to its parents

event propagation

Event Delegation

  • dynamically creating an element
addNewButton.addEventListener('click', function (i) {
  var newButton = document.createElement('button');
  newButton.className = 'button';
  newButton.setAttribute('id', 'new-button')
  newButtonkjl.setAttribute('data-index', i);
  newButton.textContent = "New click me button!";
  parentDiv.appendChild(newButton);
});
  • accessing an element that has been dynamically created
parentDiv.addEventListener('click', function(e) {
  if (e.target.className === 'button') {
    console.log(e)
  } 
})

Local Storage, JSON, & Data Attributes

Local Storage

  • client side storage
  • unique to each url
  • stored indefinitely
  • can only accept strings
  • Methods: setItem(), getItem(), removeItem(), clear(), key(), length
  • setItem('key', value)
localStorage.setItem('pizzas', JSON.stringify(pizzasArray));
  • getItem('key')
JSON.parse(localStorage.getItem('pizzas'));
  • removeItem('key')
localStorage.remove('pizzas');
  • clear()
localStorage.clear();
  • key(n)
localStorage.key(0); // returns pizzas

JSON

  • fancy string that is very similar to a JavaScript object

Data Attributes

Functions

Function Declaration vs. Function Expression

  • Will act pretty much the same except for some instances of hoisting.

  • Hoisting has to do with how the interpreter processes your JavaScript. Function declarations and their entire value are hoisted, whereas function expressions have only the variable name hoisted to the top.

  • If you try to call a Function Expression before it is defined, it won't work.

  • Function Declaration

function printName() {
    //statements
}
  • Function Expression
const printName = function() {
    //statements
}
  • The 'return' keyword will return a value from a function, otherwise a function will return 'undefined'
function sayHi() {
  return 'Hey there partner!';
}
  • Functions can have parameters which will accept arguments
function greeting(name) {
  return `Hey there ${name}, pleasure to meet ya!`;
}
  • function return values can be saved into variables for later use
const nameGreeting = function(name) {
  return `Hey there ${name}, pleasure to meet ya!`;
}

console.log(nameGreeting);

Objects

  • object literal
const band = {
  name: 'Toto',
  formedIn: 1976,
  debutAlbum: {
    title: 'Toto',
    year: 1978
  },
  topSongs: [
    'Africa',
    'Hold the Line',
    'Rosanna'
  ]
}
  • Dot Notation can be used for accessing an object and setting properties
band.name; // Toto

band.members = [
  'Joseph Williams (lead vocals)', 
  'David Paich (keyboards, vocals)',
   'Steve Porcaro (keyboards)',
   'Steve Lukather (guitars, vocals)'
];
  • Bracket Notation can also be used for accessing and setting properties
band['name']; // Toto

band['members'] = [
  'Joseph Williams (lead vocals)', 
  'David Paich (keyboards, vocals)',
   'Steve Porcaro (keyboards)',
   'Steve Lukather (guitars, vocals)'
];
  • Bracket Notation is primarily used when you are storing the property key in a variable
var storeItems = [ 'hammer', 'chisel', 'rake' ];
var store = {
	hammer: 5,
	chisel: 3,
	rake: 7
};

var totalCost = 0;

for (var i = 0; i < storeItems.length; i++) {
	var itemName = storeItems[i];
	var itemPrice = store[ itemName ];  // using bracket notation because property is changing

	totalCost += itemPrice;
};

console.log(totalCost);
  • Bracket Notation is used when adding many properties at once with dynamic key names
const library = {};

var i = 1;
while (i <= 200) {
  let book = `Book${i}`;
  library[book] = i;
  i++;
}

console.log(library);

Object Methods

Object.keys()

  • returns an array of keys in an object
  • used for iterating over objects
const pizza = {
  crust: 'thin',
  extraCheese: true,
  toppings: ['pepperoni', 'jalapeno'],
  sauce: 'red'
}

Object.keys(pizza) // ['crust', 'extraCheese', 'toppings', 'sauce']

Object.assign()

  • make a copy of an existing object, or merge multiple objects together
  • syntax: Object.assign(targetObject, sourceObject1, sourceObject2)
  • when creating a new object syntax: const newObj = Object.assign({}, sourceObject1, sourceObject2);
  • there can be multiple source objects, if properties are the same, the latter source object will take precedence
const grayPizza = {
  crust: 'thin',
  extraCheese: true,
  toppings: ['pepperoni', 'jalapeno'],
  sauce: 'red'
}

const pamPizza = {
  crust: 'thick',
  extraCheese: false,
  toppings: ['ham', 'pinneapple'],
  sauce: 'white'
}

// make a copy of grayPizza and assign it to mikePizza
const mikePizza = Object.assign({}, grayPizza); 

// if you don't make a copy, any changes will apply to both pizzas
mikePizza.sauce = 'vodka'

Arrays

  • Arrays are an object abstraction for creating lists of similar information. Each item in an array is assigned to a unique index.

Array Methods

Array.isArray(value) can be used to check if something is an array
  • 3 catories of Array prototype methods:
    1. Mutator - directly modify the existing array (adding or removing items, sorting). Usually returns the mutated array.
    2. Accessor - do not modify the existing array (return a particular representation or the array, ex. join() or slice()).
    3. Iterator

Array.slice()

  • accessor method, does not mutate the original array
  • two optional arguments:
    1. index at which the beginning of the array is cut off (does not cut off that index). Default 0.
    2. index at which the slicing stops (does not keep that index). Default is to the end of the array.
var animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]

console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]

console.log(animals.slice(1, 5));
// expected output: Array ["bison", "camel", "duck", "elephant"]

Array.splice()

  • mutates the original array
  • takes 3 main arguments:
    1. start
    2. delete count
    3. itemToInsert1, itemToInsert2, itemToInsert3
var months = ['Jan', 'March', 'April', 'June'];

months.splice(1, 0, 'Feb');
// inserts at 1st index position
console.log(months);
// expected output: Array ['Jan', 'Feb', 'March', 'April', 'June']

months.splice(4, 1, 'May');
// replaces 1 element at 4th index
console.log(months);
// expected output: Array ['Jan', 'Feb', 'March', 'April', 'May']

Array.from()

  • creates a new, shallow-copied Array instance from an array-like or iterable object.
  • 3 possible arguments:
    1. first is arrayLike object you wish to convert into an array,
    2. optional map function for each item,
    3. optional this keyword binding when using the map function
Array.from(arrayLike[, mapFn[, thisArg]])

Questions to ask yourself for iterator methods

  • What parameters does it take in? (callback and maybe something else)
  • What parameters does the callback take in?
  • What does the callback evaluate to? (Filter callback evaluates to a boolean)
  • What does the method return?

.forEach Method

The .forEach method iterates through an array and runs a callback function on each element inside the array. forEach always returns 'undefined'. forEach callback can take 4 possible parameters: the value (required), index, array, and thisArg. We do not always need all three parameters, we can use whichever ones we need, but the order is important.

[1, 2, 3].forEach(function(value, index, array, thisArg) {
    return value * 2;
});

// returns 2, 4, & 6 individaully

.map Method

The .map method iterates through an array, runs a callback function on each element in that array, creates a new array with the results of that callback function, the new array is then returned. The result of Map is an array that is always the same length as the original array.

  • Callback Parameters: value, index(optional), array(optional), thisArg(optional)
  • Example 1
[1, 2, 3].map(function(value, index, array, thisArg) {
    return val * 2;
});

// returns [2, 4, 6]

.map callback takes an optional second argument that allows you to specify the context in which you would like the function to be invoked in.

const languages = ['JavaScript', 'Ruby', 'Python'];
languages.map(function(lang) {
    <li onClick={this.updateLanguage}> {lang} </li>
}, this);

// the second argument is passed after the callback

.filter Method

  • filter returns a new array with elements that return true in the callback condition
  • the callback block is a conditional itself
  • must use the return keyword
  • callback parameters: item, index, array
[2, 4, 5, 12, 18].filter(function(num) {
    return num > 10;
});

// [12, 18]

.find Method

  • same as filter, but stops the function when a match is met.

.sort Method

  • can be called with no arguments on strings to sort() alphabetically
  • when used with (a, b), swaps numbers if the block returns a negative number
  • sort mutates the original array
var numbers = [7, 1, 10, 3]
numbers.sort(function(a, b) {
    return a - b;
}
numbers; // [1, 3, 7, 10]
  • if we want to sort in reverse order, switch return to b - a
var numbers = [7, 1, 10, 3]
numbers.sort(function(a, b) {
    return b - a;
}
numbers; // [10, 7, 3, 1]

.some & .every Methods

.reduce Method

  • reduce is typically used to take an array of items and return one value

  • the value returned from reduce can be a number, array, or object

  • reduce takes in two values, a callback and an initail value

  • The callback (reducer) function has 2 parameters (accum, item), and 2 more optional params (index, array)

  • good practice to name the new variable you are creating and the accumulator the same thing

  • callback function must always return the accumulator

  • EX1, using reduce to sum the numbers in an array

const scores = [89, 76, 47, 95];
const total = scores.reduce(function (accumulator, item) {
    return accumulator + item;
}, 0);

const average = total / scores.length;
  • EX2, using reduce to tally the occurances of items in an array and put the results into an object
const votes = ['tacos', 'pizza', 'pizza', 'tacos', 'fries', 'ice cream', 'ice cream', 'pizza']

const result = votes.reduce(function (obj, vote) {
	if(!obj[vote]) {
		obj[vote] = 1;
	} else {
		obj[vote] = obj[vote] + 1;
	}
	return obj;
}, {});

Scope

  • Scope refers to where variables and functions are accessible, and in what context the code is being executed.
  • Scope is like a house of rooms. The house is the global scope. Each room (function, block) creates a new local scope nested inside the global scope.
  • Nested scopes have access to things inside their parent scope, however parent scope or other sibling scopes do not necessarily have access to other local scopes.
  • local scope variable declarations will override parent scopes within that local scope

Types of Scope

  • Global Scope:
    1. Default scope
    2. Everyone has access
    3. Functions or variables in the global scope are vulnerable to being potentially mutated or overwritten
  • Function Scope:
    1. Functions or variables (var, let, const) declared inside a function can only be accessed inside that function
  • Block Scope:
    1. Variables (let, const) declared inside an 'if statement', 'for loop', 'while loop', 'switch statements' are only available within those blocks.
  • Eval Scope:
    1. Can be dangerous, vulnerable to hacks, almost never use.
    2. running a string with javascript code.
    3. some JSON parsing methods use this scope.

Closures

  • A closure is an inner function that has access to the 'enclosing' function's varibales.
  • The scope chain is the chain the code follows when it is looking for its variables: if a variable is referenced but not declared in a block, the compiler goes out to the next available scope and looks there. This process continues until the the variable declaration is found
function init() {
  var name = 'Turing';

  function displayName() {
    console.log(name)
  }

  displayName();    
}
init();

Hoisting

  • variable declarations and function declarations get 'hoisted' to the top of their scope.
  • variable assignments do not get hoisted!
let message = 'hey'

function sayHi() {
  console.log(message);
  let message = 'hey there'
}

sayHi();

// the message console.log will return undefined because the declaration gets hoised above the console.log!
  • Closures are useful as gates to protect acces local varibales
  • This next example only allows the global scope to interact with 'count' throught the add & getCount methods
function makeCounter () {
  var count = 0;

  return {
    add: function () {
      count++;
    },
    getCount: function () {
      return count;
    }
  };
}

console.log(count) // undefined

var counter = makeCounter();

console.log(counter.getCount()); // 0
counter.add();
console.log(counter.getCount()); // 1

IIFE (Immediately invoked function expression)

  • function that is wrapped in parentheses and then immediately invoked, after it is invoked it dissapears unless saved to a variable;
(function myScope () {
  var functionScopedVariable = "Safety!"
})();

console.log(functionScopedVariable); // undefined
console.log(myScope) // undefined

Keyword 'this'

What is 'this'?

  • A reserved keyword in JavaScript.
  • This refers to the object that is executing the current function.
  • ES5 functions (this is determined when function is invoked), ES6 Arrow functions (this is determined when the function is declared, does not reset the value of this)
  • When a function is called, 2 special keywords are given to that funciton: arguments and this
  • Can be determined using four rules (global/window binding, object/implicit binding, explicit binding, new binding).

1. Global Context

  • When 'this' is not inside of a declared object, it refers to the global object, typically 'window'
console.log(this); // window
  • returning 'this' inside of a function will still be the global object
  • 'this' is still not inside of a declared object
function whatIsThis() {
    return this;
}
whatIsThis(); // window
  • it's very bad practice to declare global variables inside of a function
function variablesInThis() {
    this.person = 'Boolean Brandon';
}
variablesInThis() // 'this' inside the function is still the window
// person is now a global variable assigned to 'Boolean Brandon' even though it was assigned inside a function
  • "use strict" when typed at the top of a file, will throw an error if you try to declare a global variable inside a function

2. Implicit / Object (most common)

  • when the keyword 'this' is inside of a declared object, its value will always be the closest parent object
const person = {
    firstName: 'Brian',
    sayHi: function() {
        return "Hi " + this.firstName;
    }
}

person.sayHi();
// this refers to person;
  • The keyword 'this' is only re-defined when a function is run! In the next example the keyword 'this' still refers to the global object.
const person = {
    firstName: 'James',
    determineContext: this;
}

person.determineContext; // window
  • Nested Objects
const person = {
    firstName: 'Gray',
    sayHi: function() {
        return "hi " + this.firstName;
    },
    dog: {
        firstName: 'Winnie',
        sayHello: function() {
            return 'hello ' + this.firstName;
        }
    }
};

person.sayHi(); // "hi Gray";
person.dog.sayHello(); // "hello Winnie"

// always look to the left of the dot, thats usually what 'this' is referring to

3. Explicit Binding

  • Choose what we want to the context of 'this' to be using call, apply, or bind
  • call, apply, and bind can only be invoked on functions
  • call, apply, and bind have precedence over the first 2 rules
  • call and apply will invoke the function that they are called on immediately, whereas bind will return a new function definition with the value of the keyword this explicitly set
  • call and bind accept an infinite number of parameters, whereas apply only takes in 2 parameters

Call

  • call can be used to bind 'this', when defined in a stand alone function, to an object that it is invoked on. '
const sayName = function() {
    console.log('My name is ' + this.name);
}

const gray = {
    name: 'Gray',
    age: 32
};

const alex = {
    name: 'Alex',
    age: 38
}

sayName.call(gray); // 'My name is Gray'
sayName.call(alex); // 'My name is Alex'
  • Call can also enable you to use a funciton defined in one object on another object.
const kurt = {
    firstName: 'Kurt',
    sayHi: function() {
        return 'Hi ' + this.firstName;
    }
}

const kevin = {
    firstName: 'Kevin'
}

kurt.sayHi(); // Hi Kurt
kurt.sayHi.call(kevin); // Hi Kevin
  • The first argument passed in to call explicitely states what to bind the keyword 'this' to. Every argument after that are the arguments that are passed to the actual function being invoked.

Apply

  • Apply is the exact same as call except call passes arguments to the function as separate values and apply passes arguments to the function as an array.
function addNumbers(a, b, c) {
    return this.firstName + ' just calculated ' + (a + b + c);
}

const tom = {
    firstName: 'Tom'
}

addNumbers.call(tom, 1, 2, 3); // Tom just calculated 6
addNumbers.apply(tom, [1, 2, 3]); // Tom just calculated 6

'* when a function does not accept an array, apply will spread out values in an array for us!

function sumValues(a,b,c) {
    return a+b+c;
}

var values = [4, 2, 1];

sumValues(values); // undefined
sumValues.apply(this, [4, 2, 1]); // 7

Bind

  • Bind works just like Call, but instead of immediately invoking the function, it returns a new function with the keyword 'this' set to the value we pass as the first parameter when calling bind.
  • Bind can be useful when we do not yet know all of the arguments that will be passed to a funciton.
  • We do not need to know all of the arguments to a funciton when we call bind, only what we want the value of the keyword 'this' to be.
function addNumbers(a,b,c) {
    return this.firstName + " just calculated " + (a+b+c);   
}

const timf = {
    firstName: "Timf"
}

const timfCalc = addNumbers.bind(timf, 1, 2, 3); // function(){}...
timfCalc(); // Timf just calculated 6
  • very commonly we lose the context of 'this', especially in functions that we do not want to execute right away.
const cody = {
    firstName: 'Cody',
    sayHi: function() {
        setTimeout(function() {
            console.log('hi ' + this.firstName;
        }, 1000);
    }
}

// the keyword this inside setTimeout actually refers to the global object, since setTimeout is called at a later point in time, the object that it is attached to is the window. The context in which setTimeout is executed is the global context.

// we could use call or apply to set the keyword 'this', but we dont want to call the function until a later point in time. So we use bind.

const cody = {
    firstName: 'Cody',
    sayHi: function() {
        setTimeout(function() {
            console.log('hi ' + this.firstName;
        }.bind(this), 1000);
    }
}

// using .bind(this) on setTimeout will bind the keyword 'this' to the parent object which is cody.

4. The 'new' keyword

  • we can set the context of the keyword 'this' using the 'new' keyword.
  • when the 'new' keyword is used, a new object is created. The new keyword is used with a function and inside the function definition, the keyword 'this' will refer to the new object that is created.
  • when the new keyword is used, an implicit return 'this' is added to the funciton which uses it.
function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

const dennis = new Person("Dennis", "Miller");

dennis.firstName; // "Dennis"
dennis.lastName; // "Miller"

'this' when using ES6 classes

class Pizza {
  constructor(crust, sauce, toppings) {
    this.crust = crust;
    this.sauce = sauce;
    this.toppings = toppings;
  }

  logThis() {
    console.log(this);
  }
}

var grandma = new Pizza('medium', 'red', 'garlic');

grandma.logThis();

'this' Recap

  • The keyword 'this' is a reserved keyword in JavaScript and its value is determined at execution.
  • It is either set using the global context, object binding, explicit binding, or the new keyword.
  • When set in the global context in a function, it is either the global object (window if in the browswer) or undefined (if we are using "strict mode").
  • When the keyword this is inside of a declared object, its value will be the closest parent object.
  • When we lose the value of the keyword 'this' that we want, we can explicitly set the value of the keyword 'this' by using call, apply, or bind.
  • We can also use the 'new' keyword to set the context of 'this'. When the 'new' keyword is used, the value of 'this' is set to be an empty object and is implicitly returned from the function that is invoked with the 'new' keyword.

Object Oriented Programming

  • Design pattern based on reusable objects
  • OOP is a programming model based around the idea of objects and blueprints which are used to create similar objects. The blueprints used to create objects are called "classes" and the objects created from the blueprints are called "instances" of that class.
  • We strive to make our classes abstract and modular, so that we can re-use classes easily and share them amongst all parts of an application.

Vocab

  • OOP - Object Oriented Programming
  • Object - A grouping of data and behavior
  • Class - A blueprint or prototype of an object
  • Subclass - A class that inherits from a parent class
  • Instance - One specific copy of an object
  • Inheritance - The practice of allowing new objects to take on the data and behavior of their parents
  • Encapsulation - Hiding the details of how an object works & grouping data and behavior
  • Message Passing - The way in which Objects talk to each other
  • SRP - Single Responsibility Principle
  • Coupling - The level of connectedness between two objects

Benefits of OOP

  • Code reusability
  • Encapsulation - values are scoped to the specific object
  • Design & Scalability - OOP forces programmers to meticulously plan the project. OOP is also much more maintainable for larger programs
  • Maintainable - OOP tends to be easier to modify specific pieces of the code without affecting the larger program

Principal of Least Knowledge

  • Each unit should only have limited knowledge about other units: only units 'closely' related to the current unit
  • Each unit should only talk to immediate friends

OOP in Javascript

  • Javascript does not have built-in support for classes unlike Ruby, Python, and Java. We can mimic the behavior of classes using functions and objects.
  • constructor function (use capitalized name for constructor function):
function House(bedrooms, bathrooms, numSqft) {
    this.bedrooms = bedrooms;
    this.bathrooms = bathrooms;
    this.numSqft = numSqft;
}

// we are attaching properties onto the keyword 'this'
  • using the new keyword creates a new instance of the class defined in the constructor function
const firstHouse = new House(2, 2, 1000);
firstHouse.bedrooms; // 2
firstHouse.bathrooms; // 2
firstHouse.numSqft; // 1000
  • The new keyword must be used with a constructor function or else you'll get a type error.
  • The new keyword takes several actions when being created:
  1. The 'new' keyword first creates an empty object.
  2. It then sets the keyword 'this' in the constructor function to be that empty object that was just created.
  3. It adds an implicit 'return this' to the end of the function, so that the object created using the 'new' keyword can be returned from the function.
  4. It adds a property onto the empty object called "proto", which links the object that was just created to the prototype property on the constructor function. This property is commonly called "dunder proto".
function Dog(name, age) {
    this.name = name;
    this.age = age;
    this.bark = function() {
        console.log('this.name ' + just barked!);
    }
}

const winnie = new Dog('Winnie', 1);

winnie.bark(); // Winnie just barked!

Multiple Constructors

  • here are 2 constructor functions, one for a car, and one for a motorcycle:
function Car(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.numWheels = 4;
}

function Motorcycle(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.numWheels = 2;
}

There is a lot of duplicate code in the Motorcycle function, it'd be great if we could borrow some of that from the Car function and invoke it in the Motorcycle function. We can refactor the code using call and apply!

function Car(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.numWheels = 4;
}

function Motorcycle(make, model, year) {
    //using call
    Car.call(this, make, model, year)
    this.numWheels = 2;
}
function Motorcycle(make, model, year) {
    //using apply
    Car.apply(this, [make, model, year]);
    this.numWheels = 2;
}

// the keyword 'this' in the Motorcycle function will now refer to the new object being created when the 'new' keyword is used to create a new instance of Motorcycle.
function Motorcycle(make, model, year) {
    //using apply
    Car.apply(this, arguments);
    this.numWheels = 2;
}

// for apply, we can either list the parameters in an array, or make use of another special keyword called 'arguments'. 'arguments' is a list of all of the arguments that are passed to a function. 'arguments' is similar to but not technically an array. It is an array like object.

Constructor Functions Recap

  • Object Oriented Programming is a model based on objets constructed from a blueprint. We use OOP to write more modular and shareable code.
  • In languages that have build in support for OOP (ruby, python, java), we call these blueprints 'classes' and the objects created from them are called 'instances'.
  • Since Javascript does not have build in support for OOP, we can mimic 'classes' and 'instances' using constructor functions, which create objects through the use of the 'new' keyword.
  • The 'new' keyword does 4 things:
    1. It creates an empty object from scratch.
    2. It sets the value of the keyword 'this' in the constructor function to be that new object that was just created.
    3. It adds an implicit 'return this' to the constructor function so that the new object properties can be returned.
    4. It sets a property on the newly created object which we can access using "proto", which connects the newly created object to the prototype property of the constructor function.
  • We can avoid duplication in multiple constructor functions by using call or apply to change the context of the keyword 'this'.

Prototypes

  • Every constructor function has a property on it called "prototype", which is an object.
  • The prototype object has a property on it called "constructor", which points back to the constructor function.
  • Anytime an object is created using the 'new' keyword, a property called "proto" gets created, linking the objects and the prototype property on the constructor function.
// constructor function
function Person(name) {
    this.name = name;
}

// objects created from the Person constructor using the 'new' keyword
const laura = new Person('Laura');
const justin = new Person('Justin');

// when the 'new' keyword is used, a property called 'dunder proto' is added to the newly created object and is equal to the prototype property on the constructor function
laura.__proto__ === Person.prototype; // true
justin.__proto__ === Person.prototype; // true

Person.prototype.constructor === Person; // true

The .prototype property of the constructor function is an object which can have methods and properties placed on it. These methods and properties are accessable to any object that is created from the constructor function using the 'new' keyword.

Person.prototype.isInstructor = true;

elie.isInstructor; // true
colt.isInstructor; // true

// how were these objects able to access properties on the prototype? __proto__ !!

Since these objects both have a link to person.prototype, they can both access anything inside of it. This is the way that javascript finds methods and properties on objects and is called "The Prototype Chain".

Prototype Chain

  • how does Javascript find methods and properties?
const arr = new Array;
arr.push('Gray');

// where does .push come from? arr.__proto__ !!

arr.__proto__ === Array.prototype; // true
  • Every object in Javascript has a method called .hasOwnProperty('length'), which accepts a property as a parameter and if that object has access to that property, returns true.
  • Javascript will keep going up the prototype chain to see if an object has access to a certain property: arr.proto -> Array.prototype (.proto) -> Object.prototype (.proto) -> null

Adding methods to the Prototype (Refactoring)

Now that we know the objects created by the same constructor have a shared prototype, let's refactor some code:

function Person(name) {
    this.name = name;
    this.sayHi = function() {
        return 'Hi ' + this.name;
    }
}

elie = new Person('Elie');
elie.sayHi(); // Hi Elie

This code works, but is inefficient. Everytime we make an object using the new keyword we have to redefine the sayHi function! It would be more efficient if we could define sayHi once and have it shared amonst all objects created from the Person constructor when the 'new' keyword is used. Let's define the function on the prototype instead!

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

Person.prototype.sayHi = function() {
    return 'Hi ' + this.name;
}

elie = new Person('Elie');
elie.sayHi(); // Hi Elie

Prototypal Inheritance

  • The passing of methods and properties from one class to another.
  • In Javascript this means passing the prototype property of one constructor function to another constructor function.
Object.create
  • Object.create creates a brand new function and accepts what the prototype object (dunder proto) should be for the newly created object as its first parameter.
function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

Person.prototype.sayHi = function() {
    return 'Hello ' + this.firstName + ' ' + this.lastName;
    
function Student(firstName, lastName) {
    return Person.apply(this, arguments);
}

// student prototype is assigned to a new object with a dunder proto of Person.prototype
Student.prototype = Object.create(Person.prototype);

Student.prototype.status = function() {
    return 'I am currently a student!';
}

const elie = new Person('Elie', 'Schooner');

// student prototype does not affect person
elie.status; // undefined

Why use Object.create instead of the 'new' keyword?

  • The new keyword does the same thing as Object.create, but it will add additional unnecessary properties on the prototype object (since it is creating an object with undefined properties just for the prototype).
  • So when doing inheritance through the prototype (prototypal inheritance), use Object.create.

One missing peice...

  • If look at the constructor property on Studen.prototype it is the Person constructor.
  • When we used Object.create, we overwrote the constructor property, so we need to set it back to the correct value.
  • this is the final step in prototypal inheritance.
Student.prototype.constructor; // Person
Student.prototype.constructor = Student;

OOP Recap

  • Every time the 'new' keyword is used, a link between the object created and the prototype property of the constructor is created (this link can be accessed using proto or 'dunder proto')
  • This link allows objects created from the constructor function to access methods and properties on the prototype object.
  • The prototype object also contains a property called constructor, which points back to the constructor funciton.
  • We should strive to place all properties and methods that we want to be available to objects created with a constructor function inside of the constructor function prototype. Since these properties and methods only need to be defined once, it is more efficient to define them in the prototype and not the constructor function itself otherwise they would be redefined everytime a new object is created.
  • We can pass methods and properties from one prototype object to another using Prototypal Inheritance. This process involves setting the prototype property to be a newly created object using Object.create and then resetting the constructor property on that object.

OOP Using ES6 Classes

  • es6 introduced classes to make OOP in javascript easier
  • Classes are templates for object instances
  • A class constructor method is called when a new instance is created
  • Classes will have a constructor which sets instance properties
  • Prototype methods can be defined outside of the constructor
class Dog {
  constructor(name) {
    this._name = name;
    this._behavior = 0;
  }

  get name() {
    return this._name;
  }
  get behavior() {
    return this._behavior;
  }   

  incrementBehavior() {
    this._behavior ++;
  }
}
  • You can use the 'new' keyword to create an instance of a class the same way you would with a constructor function.
  • The constructor method of a class will be invoked every time a class is instantiated
  • The context of this inside the constructor method will refer to the new instance of the class being created
const halley = new Dog('Halley');
  • Getters are methods that return properties of an object instance
  • There are no comma's in between methods in a class
  • Properties are prepended with underscores indicating that these properties should not be accessed directly, but instead through getter methods
class Surgeon {
  constructor(name, department) {
    this._name = name;
    this._department = department;
    this._remainingVacationDays = 20;
  }
  
  get name() {
    return this._name;
  }
  
  get department() {
    return this._department;
  }
  
  get remainingVacationDays() {
    return this._remainingVacationDays;
  }
  
  takeVacationDays(daysOff) {
  	this._remainingVacationDays = this._remainingVacationDays - daysOff;
  } 
}
  • Calling methods on class instances uses dot notation and parentheses
const surgeonCurry = new Surgeon('Curry', 'Cardiovascular');
surgeonCurry.takeVacationDays(3);

Inheritance using 'Super'

  • When multiple classes share properties and methods they become candidates for inheritence
  • Inheritance will include a parent class (superclass) and multiple child classes, which will inherit properties and methods from their parent class
  • The 'extends' keyword will make the methods of a parent class available to a child class
  • The 'super' keyword calls the constructor of the parent and extends its properties to the child
  • The 'super' keyword will accept the arguments of the instance properties you wish to extend
  • Child classes will automatically inherit parent methods
class HospitalEmployee {
  constructor(name) {
    this._name = name;
    this._remainingVacationDays = 20;
  }
  
  get name() {
    return this._name;
  }
  
  get remainingVacationDays() {
    return this._remainingVacationDays;
  }
  
  takeVacationDays(daysOff) {
    this._remainingVacationDays -= daysOff;
  }
}

class Nurse extends HospitalEmployee {
  constructor(name, certifications) {
    super(name);
    this._certifications = certifications;
  }
}

const nurseOlynyk = new Nurse('Olynyk', ['Trauma', 'Pediatrics']);

nurseOlynyk.takeVacationDays(5);
console.log(nurseOlynyk.remainingVacationDays);
  • Child classes can have their own getter and setter methods
  • Getter methods return the value of a property
  • Setter methods change the value of a property
class Nurse extends HospitalEmployee {
  constructor(name, certifications) {
    super(name);
    this._certifications = certifications;
  } 
  
  get certifications() {
    return this._certifications;
  }
  
  addCertification(newCertification) {
    this._certifications.push(newCertification);
  }
}

Static / Class methods

  • Static methods are not available to individual instances, but to the class itself
  • Static methods are createde using the 'static' keyword
  • .now() is an example of a static method on the Date javascript object
class HospitalEmployee {
  constructor(name) {
    this._name = name;
    this._remainingVacationDays = 20;
  }
  
  static generatePassword() {
    return Math.floor(Math.random()*10000);
  }
}

HospitalEmployee.generatePassword();

Creating a BoilerPlate

NPM - Node Package Manager

  • A package is a bit of reusable code

NPM Commands

NPM Command Functionality
npm init Initializes npm and creates the package.json file
npm init --yes run you through a dialogue designed to help customize the package.json file
npm install Install package dependancies and devDependencies listed in package.json
npm install [package-name] Install package locally in folder location node_modules
npm install -g [package-name] Install package globally, usually to /usr/local/lib/node_modules
npm install --save [package-name] Install package locally in folder location node_modules and update package.json dependancies
npm install --save-dev [package-name] Install package locally in folder location node_modules and update package.json developer dependancies
npm start Run start script located in package.json
npm test Run test script located in package.json
npm run [custom script] Run custom script located in package.json

Webpack

  • Webpack is a build tool that takes multiple JavaScript modules and bundles them up into a single, unified file.
  • We can organize our code into separate files. This makes it easier to find specific pieces of code and improves maintainability.
  • Webpack creates a unique scope for each our files, helping to prevent adding things to the global and naming collisions.
  • By bundling all of our JS into a single file, the browser only needs to request, wait for, and process through one file.

Configuring Webpack

  • Basic webpack.config.js config:
 const path = require('path');

 module.exports = {
   devtool: 'inline-source-map',
   entry: {
     main: "./lib/index.js"
   },
   mode: 'development',
   output: {
     path: __dirname,
     filename: "dist/[name].bundle.js"
   },
   module: {
     rules: [
       {
         test: /\.css$/,
         exclude: /node_modules/,
         loader: "style-loader!css-loader"
       },
       {
         test: /\.js$/,
         loader: 'babel-loader',
         query: {
           presets: ['es2015']
         }
       }
     ]
   },
   resolve: {
     extensions: ['.js', '.json', '.css']
   }
 };

devtool

This provides a map of our bundled code, so that when we run into errors it will tell us in the console where the error is in our pre-bundled code.

entry

This is where webpack will start bundling up our app. It should be the entry point to your application.

output

This defines where where webpack will create your bundled code and what it will name the file.

loaders

Loaders transform different non-JS code into valid JS modules so they can be included when webpack bundles everything up into a single file for the browser.

Loaders have two parts: an npm module, and a configuration object which is added to the webpack.config.js file.

Test Config

  • Basic test/index-test.js file config
import { assert } from 'chai';
    
 describe('test', function() {
   it('should return true', function() {
     assert.equal(true, true);
   });
 });

ESLint

Linting is checking code for consistency and synctactical cleanliness. It can also help us spot things like redundant or extraneous code (e.g. variables we declare but never use, etc).

Configuring ESLint

  • create: .eslintrc
  • in .eslintrc:
 {
   "extends": "eslint:recommended",
   "env": {
     "browser": true,
     "mocha": true,
     "node": true,
     "es6": true
   },
   "parserOptions": {
     "sourceType": "module"
   },
   // Having a problem with one of these rules? Learn more about it here: https://eslint.org/docs/rules/
   "rules": {
     "brace-style": "error",
     "comma-spacing": [
       "warn", {
         "before": false,
         "after": true
       }
     ],
     "curly": "error",
     "eqeqeq": ["error", "always"],
     "getter-return": ["error", { "allowImplicit": true }],

     "indent": ["warn", 2],
     "key-spacing": [
       "error", {
         "beforeColon": false,
         "afterColon": true
       }
     ],
     "keyword-spacing": [
       "error", {
         "before": true,
         "after": true
       }
     ],
     "linebreak-style": [
       "error",
       "unix"
     ],
     "max-len": [
       "error",
       80
     ],
     "new-cap": [
       "error", {
         "newIsCap": true
       }
     ],
     "newline-after-var": [
       "error",
       "always"
     ],
     "no-template-curly-in-string": "error",
     "object-shorthand": [
       "error",
       "always"
     ],
     "semi": ["error", "always"],
     "semi-spacing": [
       "error", {
         "before": false,
         "after": true
       }
     ],
     "space-before-blocks": [
       "error", {
         "functions": "always",
         "keywords": "always",
         "classes": "always"
       }
     ],
     "space-infix-ops": [
       "error", {
         "int32Hint": false
       }
     ]
   },
   "globals": {
     "expect": true
   }
 }
  • in the "scripts" object in package.json:
"eslint": "./node_modules/eslint/bin/eslint.js ./lib/*.js"
  • in terminal: 'npm run eslint'

Creating an NPM Module

Steps for Creating an NPM module: 1. NPM init

SemVer (Semantic versioning)

  • '0.1.0'
First Number Second Number Third Number
Breaking Changes New Feature Bug Fixes

Javascript Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment