Objects Revisited, Prototypes

CSCI-UA.0480-008

Object-Oriented Programming

Describe the following object oriented programming concepts: →

  • inheritance - basing a class off of another class so that it maintains the same behavior as its super class
  • polymorphism - having the same interface to instances of different types
  • encapsulation - information hiding, internals/implementation details not accessible publicly
  • abstraction - (again) creating tools / interfaces that allow a programmer to address the actual problem they're solving rather than having to deal with necessary, but irrelevant (to the problem) details

Object-Oriented Programming in Java

In Java, what language features / constructs allow inheritance, polymorphism, encapsulation, and abstraction?

  • inheritance - subclassing… using extend
  • polymorphism - instances of different classes that are subclasses of the same superclass
  • encapsulation - private methods and private members variables
  • abstraction - creating classes, interfaces, abstract classes, and methods

Object-Oriented Programming in JavaScript

Although JavaScript has objects, its approach to object-oriented programming is a bit unconventional.

  • it still supports encapsulation, inheritance, polymorphism, and abstraction
  • … but it does so differently than Java (and other languages that support classical object-oriented techniques) →


  • inheritance - prototypes and/or functions
  • polymorphism - duck typing
  • encapsulation - closures
  • abstraction - higher order functions, prototypes, etc.

An Aside on Duck Typing

If it looks like a duck and it quacks like a duck… it's a duck

  • when an object's methods and properties determine valid semantics
  • … rather than its class or the class that it inherits from

duck

Let's dive into objects

(ouch)

Globals

First off, in both Node and browser-based JavaScript implementations a global object exists:

  • global for node
  • window for browsers


Let's see what this looks like by: →

  • checking out global in the interactive shell
  • inadvertently creating a global variable within a function definition (dropping const, let, and var)

console.log(global.mistake);
function oopsGlobal() {
	mistake = "yup";
}
oopsGlobal();
console.log(mistake);
console.log(global.mistake);

Methods

Methods are object properties that are functions (a function within the context of an object).


const cat = {};
cat.speak = function() {
	console.log("meow"); 
};
cat.speak();

Notice that you can attach methods to any arbitrary object instance! (???)

This

"When a function is called as a method—looked up as a property and immediately called, as in object.method()—the special constiable this in its body will point to the object that it was called on". What will be printed out in the code below?.


function speak() {
	if(this.nationality == "Japanese") {
		console.log("nyan");
	} else if (this.nationality == "American") {
		console.log("meow");
	} else {
		console.log("default cat noise");
	}
}

const japaneseCat = {nationality:"Japanese", speak:speak};
const americanCat = {nationality:"American", speak:speak};

japaneseCat.speak();
americanCat.speak();

nyan
meow

In methods, this refers to the object that the method was called on

Hm. What if a Function isn't Attached to an Object?

A standalone function's this refers to the global object. What will the following code print out?


global.outside = 5;
const f = function() {
	console.log(this.outside);
}
f();

5

// this is the global object!

Standalone Functions and This

Aaaand… what's the output of our speak function from the previous slide if we call it on its on (not within the context of an object)?


function speak() {
	if(this.nationality == "Japanese") {
		console.log("nyan");
	} else if (this.nationality == "American") {
		console.log("meow");
	} else {
		console.log("default cat noise");
	}
}
speak();

default cat noise
  • if we console.log(this.nationality) … we'll see it's undefined
  • nationality was not yet defined on the global object, so we get undefined

Oh. Also…

How to say meow in different languages

In standalone functions, this refers to the global object

Another Way(s)

Besides method invocation and regular function invocation, what are two other ways of executing a function?

  • call - invoke function that call was called on with specified this and positional arguments
  • apply - invoke function that apply was called on with specified this and an Array containing positional arguments


When invoking a function with call or apply:

  • this will be bound to the value passed in as the first argument.
  • What's the output of the following code?

function greet(person) { console.log(this.greeting, person); }
const obj = { greeting: 'hi' };
greet.call(obj, 'joe');

hi joe

Call and Apply Continued

Aaaand… of course, call and apply with our cat example (modified a bit). What is the output of this code?


function speak(how, toWho) {
    const d = {Japanese: "nyans", American: "meows"};
    const noise = d[this.nationality] || "default cat noise";
    console.log(noise, how, 'at', toWho);
}
const cat = {nationality: "American"}

speak.apply(cat, ['loudly', 'you']);
speak.apply({}, ['softly', 'me']);

meows loudly at you
default cat noise softly at me

When executing a function with the methods call or apply, this refers to the object passed in as the first argument to either method

Another Mystery?

What is the output of this code and why?


const counter = {numbers: [1, 2, 3, 4], animal:'owl'};

counter.count = function() {
    this.numbers.forEach(function(n) {
        console.log(n, this.animal + (n > 1 ? 's' : ''));
    });
};
counter.count();

1 'undefined'
2 'undefineds'
3 'undefineds'
4 'undefineds'

The anonymous function is being invoked as a regular function for every element in the Array, counter.numbers. this refers to global object, which does not have the property, animal, resulting in undefined.

Arrow Functions

In previous slides, we said that the this value in arrow functions is the this in the scope that the arrow function was created in (that is, it doesn't have it's own this, it just uses the one that's already there!

Let's see how this works:


const counter = {numbers: [1, 2, 3, 4], animal:'owl'};

counter.count = function() {
    this.numbers.forEach((n) => {
        console.log(n, this.animal + (n > 1 ? 's' : ''));
    });
};
counter.count();

1 'owl'
2 'owls'
3 'owls'
4 'owls'

Better! this is whatever this refers to in the count method, and because count was invoked as a method, this is the object that count was called on.

Arrow Functions Continued

Of course, that means if we use the following code, what will this refer to? What is the output of the following code?


function foo() {
    const bar = (x) => { console.log(this.qux); };
    bar();
}
foo();

undefined

this references this in the function, foo, which was invoked as a regular function. Consequently, this is the global object.

Summary

What is this?????

  1. regular function invocation
    • this is the global object
  2. method call
    • this is the object the method was called on
  3. invoked with call or apply
    • this is the first argument passed in to call or apply
  4. arrow function
    • this is this from the enclosing context

A Magic Trick

Let's try running this code…

  • have we defined any properties on the object called empty?
  • will this produce output or give us an error?

const empty = {}; 
console.log(empty.toString);
console.log(empty.toString());

[Function: toString]
[object Object]

Magic!
magic

Inherited Properties

If we started off with an empty object in the previous slide, where did the toString method come from?

  • "in addition to their set of properties, almost all objects also have a prototype"
  • "a prototype is another object that is used as a fallback source of properties"
  • "when an object gets a request for a property that it does not have…"
  • "its prototype will be searched for the property, then the prototype’s prototype, and so on"