JavaScript Refresh – A Deeper Look at Objects

OOP uses objects for program organization.

Encapsulation

  • Divide a program into small pieces with each piece managing its own state.
  • When working on one piece, you don’t need to know about the other piece.
  • Pieces of the program interact with each other through interfaces. The interfaces (a limited set of functions) help hide away the implementation.
  • The pieces are created using objects. The interface has methods and properties.
  • Properties that are part of the interface are called public.
  • Other properties and methods that are hidden from outside code are called private.
  • JS has no way to distinguish the two.
  • Some programmers prefix private properties with an underscore.

Methods

  • Methods are properties that point to function values.
  • The this variable is like a parameter.
  • The call method on functions let you pass this as the first perimeter.
  • Functions declared with the function keyword cannot access the this of the outside scope.
  • Arrow functions do not have their own this. They use the outside scope’s this.
<script>
    const carFactory =  {
        settings: {
            color: 'blue',
        },
        cars: ['mustang', 'camaro'],
        completed: [],
        buildCars: function () {
            console.log('object this', this);
            this.cars.map(function (car) {
                console.log('anonymous function as parameter this', this);
                // this.completed is undefined
                this.completed.push({
                    make: car,
                    color: this.settings.color,
                })
            });
        }
    }
    carFactory.buildCars();
</script>

Prototypes

A prototype is another object whose properties are used as a fallback source. The prototype can have a prototype object too. If a property isn’t found on the main object, the prototype is checked next. Then the prototype’s prototype and so on.

The Object.prototype object is the prototype object behind most other objects. The Object.prototype object does not have a prototype.

The toString method comes from the Object.prototype object.

The prototype object for functions is Function.prototype, and arrays have Array.prototype. These two have Object.prototype as their root prototype.

let protoRabbit = {
  speak(line) {
    console.log(`The ${this.type} rabbit says '${line}'`);
  }
};
let killerRabbit = Object.create(protoRabbit);
killerRabbit.type = "killer";
killerRabbit.speak("SKREEEE!");
// → The killer rabbit says 'SKREEEE!'

You can define methods in an object expression using shorthand. The shorthand creates a property called speak and gives it a function as its value.

Classes

This is the old way of making classes.

function Rabbit(type) {
  this.type = type;
}
Rabbit.prototype.speak = function(line) {
  console.log(`The ${this.type} rabbit says '${line}'`);
};

let weirdRabbit = new Rabbit("weird");

Class Notation

Before 2015, you created classes using a constructor function with a prototype property. Now, we can use the class keyword and an actual function called constructor. We can write methods in the class too.

class Rabbit {
  constructor(type) {
    this.type = type;
  }
  speak(line) {
    console.log(`The ${this.type} rabbit says '${line}'`);
  }
}

let killerRabbit = new Rabbit("killer");
let blackRabbit = new Rabbit("black");

Currently, you can only add methods in the class declaration. You have to add anything other than a function after.

Overriding Derived Properties

It’s possible to override properties.

Maps

In programming, maps are a data structure that associate one value with another. We can use an object to associate names with ages, but using plain objects as maps is dangerous because of prototypes.

One alternative is to create an object with no prototype.

const clean = Object.create(null);

JavaScript has a native class called Map that is specifically used for creating map structures. The Map class has “set”, “get”, and “has” methods.

Use this structure to update and search a large set of values.

Polymorphism

The String function actually calls the toString method. Some of the prototypes implement different toString methods.

Polymorphic code can work with values of different shape. The toString method works on strings, objects, and arrays.

Symbols

Symbols provide a solution to a problem programmers don’t run into very often.

Symbols are values created with the Symbol function. Newly created symbols are unique. You cannot create the same symbol twice. The string you pass to the Symbol function is just to help you recognize it.

Symbol(“name”) === Symbol(“name”)

The Iterator Interface

An object is iterable if it has a method named with the Symbol.iterator symbol.

Getters, Setters, and Statics

Interfaces can have properties with non-function values.

It’s possible to run a method when accessing an object’s property normally, without parenthesis.

class Temperature {
  constructor(celsius) {
    this.celsius = celsius;
  }
  get fahrenheit() {
    return this.celsius * 1.8 + 32;
  }
  set fahrenheit(value) {
    this.celsius = (value - 32) / 1.8;
  }

  static fromFahrenheit(value) {
    return new Temperature((value - 32) / 1.8);
  }
}

let temp = new Temperature(22);
console.log(temp.fahrenheit);
// → 71.6
temp.fahrenheit = 86;
console.log(temp.celsius);
// → 30

Use the set keyword to create one of these getter methods.

There’s also a set keyword that can be used to call a method when a property is set.

In a class declaration, you can use the keyword static to make a method accessible from the constructor.

Inheritance

It’s possible to extend a class. The new class inherits all the properties from the old class.

You have to call the super-class’s constructor inside of the new Class’s constructor to initialize the new class instance. Use the super keyword to do that.

Inheritance lets us build classes that are slightly different from other classes.

Encapsulation and polymorphism are used to separate code, but inheritance ties code together, making it more controversial.

Inheritance should not be the first tool you choose.

The Instance of operator

We can use instanceOf to see if an object is a child of another object.