JS 129: Notes Review

Topics of Interest

Objects

{ key: value } // property (state)
{ doThis: function() {}} // method (behavior)

Object factories

function carFactory(make, model, year) {
return {
make,
model,
year,
startEngine() {
console.log('vroom');
},
};
};
let myCar = carFactory('honda', 'crv', 2013);
myCar.startEngine() // logs "vroom"
  • you can’t use the instanceof operator to determine the object type
  • it copies all the methods & properties into the new object, taking up extra memory and creating redundant code.
  • possible to have private state. i could create and define a variable within my function that is not made available in the returned object.

Constructors

// no inheritance
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
Car.prototype.startEngine = function() {
console.log('vroom');
}
let myCar = new Car('honda', 'crv', 2013);
myCar.startEngine() // logs 'vroom'
// with inheritance!function Player(marker) {
this.marker = marker;
}
Player.prototype.getMarker = function () {
return this.marker;
};
function Human() {
Player.call(this, Square.HUMAN_MARKER);
}
Human.prototype = Object.create(Player.prototype);
Human.prototype.constructor = Human;
function Computer() {
Player.call(this, Square.COMPUTER_MARKER);
}
Computer.prototype = Object.create(Player.prototype);
Computer.prototype.constructor = Computer;
  1. creates a new empty object
  2. assigns new object’s [[Prototype]] property a reference to the constructor’s prototype object (the object referenced by the constructor’s prototype property.
  3. assigns this a reference to the newly created object
  4. invokes function
  5. returns new object
  • function will run like a normal function
  • this is implicitly assigned a reference to the global object
  • if no explicit return value, returns undefined
  • If the return value is an object, function will return that object instead of the newly created object of the desired type.
  • If the return value is a primitive, function will return newly created object instead of the explicit return value.

Prototypes

function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
Car.prototype.startEngine = function() {
console.log('vroom');
};
let myCar = new Car('honda', 'crv', 2013);
console.log(Object.getPrototypeOf(myCar) === Car.prototype); // true
// object prototype of myCar references function prototype of Car
console.log(myCar.hasOwnProperty('startEngine')); // false
console.log(myCar.__proto__.hasOwnProperty('startEngine')); // true

OLOO

let petPrototype = {  init(name, species) {
this.name = name;
this.species = species;
return this;
},
walk() {
console.log('i\'m walkin here');
},
run() {
console.log('run forrest run!');
},
};
let myPet = Object.create(petPrototype).init('Cypress', 'dog');console.log(myPet.name); // Cypress
console.log(myPet.species); // dog
myPet.walk(); // i'm walkin here
// with inheritance
const PlayerPrototype = {
initialize(marker) {
this.marker = marker;
return this;
},
getMarker() {
return this.marker;
},
};
let Human = Object.create(PlayerPrototype);Human.init = function() {
return this.initialize(Square.HUMAN_MARKER);
};
let Computer = Object.create(PlayerPrototype);Computer.init = function() {
return this.initialize(Square.COMPUTER_MARKER);
};
  • no way to define private state
  • don’t forget to return this with init() method

ES6 classes

class Pet {  static kingdom = 'animalia'  constructor(name, species) {
this.name = name;
this.species = species;
}
static sayKingdom() {
console.log(Pet.kingdom);
}
sayName() {
console.log(this.name);
}
saySpecies() {
console.log(this.species);
}
}
let cypress = new Pet('Cypress', 'dog');
cypress.sayName(); // logs Cypress
Pet.sayKingdom(); // logs animalia
class Dog extends Pet {
static phylum = 'chordate'
constructor(name, breed) {
super(name, 'dog');
this.breed = breed;
}
static sayKingdomAndPhylum() {
console.log(`Dogs are part of the kingdom ${Pet.kingdom} and phylum ${Dog.phylum}.`);
}
sayBreed() {
console.log(this.breed);
}
}
let trout = new Dog('Trout', 'Chocolate Lab');
trout.sayName(); // trout
trout.sayBreed(); // chocolate lab
Dog.sayKingdomAndPhylum();
// Dogs are part of the kingdom Animalia
// and phylum Chordate.class
// ex 2Player {
constructor(marker) {
this.marker = marker;
}
getMarker() {
return this.marker;
}
}
class Human extends Player {
constructor() {
super(Square.HUMAN_MARKER);
}
}
class Computer extends Player {
constructor() {
super(Square.COMPUTER_MARKER);
}
}

Methods and properties

let object = {
a: 1, // property
b: function(num) { // method
console.log(num + this.a);
},
c() {
console.log(`this is a method definition using compact syntax`);
}
}
object.b(1) // logs 2
object.c()

Instance and static methods and properties

function Dog(name) {
this.name = name; // instance property
Dog.allDogs.push(this);
}
Dog.prototype.getName = function() { // instance method
return this.name;
}
Dog.allDogs = []; // static property
Dog.getAllDogs = function() { // static method
return Dog.allDogs;
}
let cypress = new Dog('Cypress');
console.log(Dog.getAllDogs());
console.log(cypress.getName());
console.log(Dog.allDogs);
console.log(cypress.name);

Prototypal and pseudo-classical inheritance

// Prototypallet animalPrototype = {
walk() {
console.log('i am walking');
}
};
let dog = Object.create(animalPrototype);
dog.speak = function() {
console.log('bark bark');
};
dog.walk(); // logs i am walking
dog.speak(); // logs bark bark
// Pseudoclassicalfunction Animal() {}Animal.prototype.walk = function() {
console.log('i am walking');
};
function Dog() {
Animal.call(this);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function() {
console.log('bark bark');
};
let doggy = new Dog();
doggy.walk();
doggy.speak();

Encapsulation

Polymorphism

class Dog {
speak() {
console.log('bark');
}
}
class Bird {
speak() {
console.log('tweet');
}
}
class Cat {
speak() {
console.log('meow');
}
}
let Animals = [new Dog(), new Bird(), new Cat()];Animals.forEach(animal => {
animal.speak();
}); // logs bark, tweet, and meow
  1. inheritance — like toString() — is polymorphism in which two different object types can respond to the same method call simply by overriding a method inherited from a superclass
class Animal {
move() {}
}
class Fish extends Animal {
move() {
console.log("swimming");
}
}
class Cat extends Animal {
move() {
console.log("walking");
}
}
// Sponges and Corals don't have a separate move method - they don't move
class Sponge extends Animal {}
class Coral extends Animal {}
let animals = [new Fish(), new Cat(), new Sponge(), new Coral()];
animals.forEach(animal => animal.move());
class Chef {
prepare(wedding) {
this.prepareFood(wedding.guests);
}
prepareFood(guests) {
// implementation
}
}
class Decorator {
prepare(wedding) {
this.decoratePlace(wedding.flowers);
}
decoratePlace(flowers) {
// implementation
}
}
class Musician {
prepare(wedding) {
this.preparePerformance(wedding.songs);
}
preparePerformance(songs) {
// implementation
}
}
class Wedding {
constructor(guests, flowers, songs) {
this.guests = guests;
this.flowers = flowers;
this.songs = songs;
}
prepare(preparers) {
preparers.forEach(preparer => {
preparer.prepare(this);
});
}
}

Collaborator objects

let cat = {
name: 'Fluffy',
makeNoise() {
console.log('Meow! Meow!');
},
eat() {
// implementation
},
};
let pete = {
name: 'Pete',
pet: cat,
printName() {
console.log(`My name is ${this.name}!`);
console.log(`My pet's name is ${this.pet.name}`);
},
};

Single vs multiple inheritance

class Bird {}  
class FlyingBird extends Bird {
fly() {}
}
class SwimmingBird extends Bird {
swim() {}
}
class Stork extends FlyingBird {}
class Parrot extends FlyingBird {}
class Penguin extends SwimmingBird {}
class Ostrich extends SwimmingBird {}
// Hmmm.... we have a problem.
// What to do with ducks and geese???
// we can't inherit from both classes!
class Duck extends FlyingBird {
swim() {}
}
class Goose extends FlyingBird {
swim() {}
}

Mix-ins

const Swimmable = {
swim() {}
}
const Flyable = {
fly() {}
}
class Stork {}
Object.assign(Stork.prototype, Flyable);
class Parrot {}
Object.assign(Parrot.prototype, Flyable);
class Penguin {}
Object.assign(Penguin.prototype, Swimmable);
class Ostrich {}
Object.assign(Ostrich.prototype, Swimmable);
class Duck {}
Object.assign(Duck.prototype, Swimmable, Flyable);
class Goose {}
Object.assign(Goose.prototype, Swimmable, Flyable);
// example 2
let Hand = {
addToHand(newCard) {
this.cards.push(newCard);
},
resetHand() {
this.cards = [];
},
showHand(caption) {
console.log(caption);
console.log("");
this.cards.forEach(card => console.log(` ${card}`));
console.log("");
},
getCards() {
return this.cards;
},
revealAllCards() {
this.cards.forEach(card => card.reveal());
},
numberOfCards() {
return this.cards.length;
},
};
Object.assign(Player.prototype, Hand);
Object.assign(Dealer.prototype, Hand);

Methods and functions; method invocation vs. function invocation

function foo() {
this.bar = 'bar';
}
foo(); // function invocation
global.bar; // bar
let foo = {
bar: function() {
console.log(this);
}
};
foo.bar(); // method invocation
// `foo` is the implicit execution context for `bar`
// { bar: [Function: bar] }

Higher-order functions

function createGreeter(language) {
switch(language) {
case: 'eng' {
return function() {
console.log('hello!');
}
}
case: 'sp' {
return function() {
console.log('hola!');
}
}
case: 'ger' {
return function() { // returns an anonymous function
console.log('guten !');
}
}
}
}
let englishGreeter = createGreeter('eng');

The global object

function myFunction(num) {
this.number = num;
}
myFunction(1);
letter = 'a';console.log(global.number); // logs 1
console.log(global.letter); // logs a

Method and property lookup sequence

let myArray = [];
let prototypeLevel1 = Object.getPrototypeOf(myArray);
let prototypeLevel2 = Object.getPrototypeOf(prototypeLevel1);
console.log(myArray.hasOwnProperty('length')); // trueconsole.log(myArray.hasOwnProperty('toString')); // false
console.log(prototypeLevel1.hasOwnProperty('toString')); // true
console.log(myArray.hasOwnProperty('hasOwnProperty')); // false
console.log(prototypeLevel1.hasOwnProperty('hasOwnProperty')); // false
console.log(prototypeLevel2.hasOwnProperty('hasOwnProperty')); // true
console.log(myArray.hasOwnProperty('cats')); // false
console.log(prototypeLevel1.hasOwnProperty('cats')); // false
console.log(prototypeLevel2.hasOwnProperty('cats')); // false

Function execution context and this

let foo = {
myFunction() {
console.log(this);
}
};
function bar() {
console.log(this);
}
foo.myFunction(); // prints out foobar(); // prints out object [global]

Implicit and explicit execution context

// implicitlet foo = {
myFunction() {
console.log(this);
}
};
function bar() {
console.log(this);
}
foo.myFunction(); // logs foo
bar(); // logs [global object]
// explicitlet foo = {
myFunction() {
console.log(this);
}
};
function bar() {
console.log(this);
}
foo.myFunction.call([]); // logs [], explicit context set to []let boundBar = bar.bind({});
boundBar() // logs {}, explicit context bound to {}

Dealing with context loss

// context loss when method is passed into another function as argument
let obj = {
a: 'hello',
b: 'world',
foo: function() {
[1, 2, 3].forEach(function(number) {
console.log(String(number) + ' ' + this.a + ' ' + this.b);
}); // function is actually executed by forEach, so context is global object, rather than “obj”
},
};
obj.foo();// orconst survivorSeasons= {
titles: ['Cook Islands', 'Micronesia', 'Pearl Islands', 'Australian Outback', 'All Stars'],
showTitle: 'Survivor',
listGames: function() {
this.titles.forEach(function(title) {
console.log(this.showTitle + ': ' + title);
});
}
};
survivorSeasons.listGames();// context loss when method is moved from another object
function repeatThreeTimes(func) {
func(); // can't use func.call(john); john is out of scope
func();
func();
}
function foo() {
let john = {
firstName: 'John',
lastName: 'Doe',
greetings: function() {
console.log('hello, ' + this.firstName + ' ' + this.lastName);
},
};
repeatThreeTimes(john.greetings); // Strips context
}
foo();// context loss when function is nested in an another function
let obj = {
a: 'hello',
b: 'world',
foo: function() {
function bar() {
console.log(this.a + ' ' + this.b);
}
bar();
},
};
obj.foo(); // => undefined undefined// hard binding
let obj = {
a: 'hello',
b: 'world',
foo: function() {
[1, 2, 3].forEach(function(number) {
console.log(String(number) + ' ' + this.a + ' ' + this.b);
}.bind(this); // permanently binding function context to this
// no matter how it's called
},
};
// context variable
let obj = {
a: 'hello',
b: 'world',
foo: function() {
let self = this;
[1, 2, 3].forEach(function(number) {
console.log(String(number) + ' ' + self.a + ' ' + self.b);
});
},
};
// arrow function
let obj = {
a: 'hello',
b: 'world',
foo: function() {
[1, 2, 3].forEach(number => {
console.log(String(number) + ' ' + this.a + ' ' + this.b);
});
},
};
// thisArg
let obj = {
a: 'hello',
b: 'world',
foo: function() {
[1, 2, 3].forEach(function(number) {
console.log(String(number) + ' ' + this.a + ' ' + this.b);
}, this);
},
};

call, apply, and bind

// with context loss
let obj = {
a: 'hello',
b: 'world',
foo: function() {
function bar(argument1) {
console.log(this.a + ' ' + this.b + ' ' + argument1);
}
bar('hi');
},
};
obj.foo(); // => undefined undefined hi// with call
let obj = {
a: 'hello',
b: 'world',
foo: function() {
function bar(argument1) {
console.log(this.a + ' ' + this.b + ' ' + argument1);
}
bar.call(this, 'hi');
},
};
obj.foo(); // => hello world hi// with apply
let obj = {
a: 'hello',
b: 'world',
foo: function() {
function bar(argument1) {
console.log(this.a + ' ' + this.b + ' ' + argument1);
}
bar.apply(this, ['hi']);
},
};
obj.foo(); // => hello world hi
// with bind
let obj = {
a: 'hello',
b: 'world',
foo: function() {
let bar = function(argument1) {
console.log(this.a + ' ' + this.b + ' ' + argument1);
}.bind(this);
bar('hi');
},
};
obj.foo(); // => hello world hi

Object.assign and Object.create

let myObject = Object.assign({}, {cat: 'meow', dog: 'woof'})
console.log(myObject); // logs {cat: 'meow', dog: 'woof'}
let inheritedObject = Object.create(myObject);console.log(inheritedObject); // logs {}
console.log(Object.getPrototypeOf(inheritedObject));
// logs {cat: 'meow', dog: 'woof'}

Built-in constructors like Array, Object, String and Number

  • Arrays created using the new keyword are passed arguments separated by commas that correspond with the array elements, or if you pass in a single number argument, it creates an empty array with the length property set to that argument. One useful application of this is with Array.prototype.fill which replaces all elements of its calling array with its argument (new Array(3)).fill(‘*’) // [‘*’, ‘*’, ‘*’]). The Array constructor still works exactly the same if you use the new key word or not! (`new Array(1,2,3)` has same results as `Array(1,2,3)`).
  • Object constructor creates new objects. Can alsobe invoked without the new keyword. All objects created with Object literal syntax inherit from Object.prototype. All arrays also have access to methods on Object.prototype. Almost all JavaScript objects, whether built-in or custom-created, inherit from Object.prototype, either directly or further down the prototype chain.
  • The Date constructor creates date object representing specific date and time. Calling Date w/o args returns object representing date of creation
    you can also call with many different types of argument.
  • String objects are created with the String constructor (new String(‘xyz’)) and are not the same as string primitives. A string primitive’s type is ‘string’, but the String object’s type is ‘object’. When you call a method on a string primitive, Javascript invisibly wraps the primitive in a string object, and uses the object to access the property to call a method. once the method has been called, JS discards the string object. When properties or methods return strings, they return primitives. It’s generally not a good idea to create string objects explicitly. String() used without the new keyword just returns a new string literal.
  • The Number and Boolean constructors work in much the same way as the String constructor. When called with new, they create Number and Boolean objects. When called without new, the Number function converts its argument to a number, and the Boolean function converts its argument to a boolean.
let emptyArray = new Array(0,1,2,3)Date.prototype.toString (‘Sat Jun 01 2019 01:15:06 GMT+0500 (Pakistan Standard Time)’)
Date.prototype.getFullYear (2019)
Date.prototype.getDay (zero-indexed representation of week day)
String(‘abc’) // ‘abc
String(undefined) // ‘undefined’
Number(‘123’); // 123
Boolean(0); // false
Boolean(‘abc’); // true

Some Specific Definitions

Variables

  • Variables declared with let, const
  • Function parameters
  • Function names
  • Class names

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store