Sunday, September 13, 2020

JavaScript Classes

0 Introduction

Object literals

  • when you create object literals, you treat each object as a unique set of properties
// object with literal notation
let uma = { 
    firstname: 'Uma', 
    lastname: 'Thurman', 
    birthdate: 'April 29, 1970',
    age(){
        let millisecs = (new Date() - new Date(this.birthdate));
        return Math.floor( millisecs / (1000*60*60*24*365) );
    }
} 

In the object-oriented paradigm, objects have: state, behaviour and identity

  • the state of the object uma is the value of the properties: firstname, lastname and birthdate
  • the object uma has identity, because another object with same state is a different object
  • the object uma has behaviour: the age function

Classes of objects

  • it is convenient to define a class of objects that shares certain properties
  • instances of the class have their own properties and their class methods
    • these own properties hold the state of the object
    • these methods are defined by the class and shared by all the instances of the class
  • for example, imagine a class named Employee that represents the employees that work for a company

JavaScript classes

  • JavaScript classes use prototype-based inheritance
  • objects belong to the same class if they inherit from the same prototype

How to define classes in JavaScript

  • In ES5, objects are instances of the same class if they were created by the same constructor function
  • In ES6, you can define classes by using a simplified syntax with class keyword

1 Classes and Factory Functions

How to define a class in JavaScript using a factory function:

  • write an object to use as prototype and a factory function that create new objects that inherit from this prototype
  • the factory function makes new objects and initializes them
  • note: this is not the idiomatic way to define a class in JavaScript

Example: defining the createPerson factory function

// place common properties into prototype object
const personPrototype = {
    age(){
        let millisecs = (new Date() - new Date(this.birthdate));
        return Math.floor( millisecs / (1000*60*60*24*365) );
    }
}
// then set the [[Prototype]] slot in the factory function
function createPerson(firstname, lastname, birthdate) {
    const result = { firstname, lastname, birthdate };
    Object.setPrototypeOf(result, personPrototype);
    return result;
}

Example: using the createPerson factory function

// create two instances of the Person class
const uma  = createPerson('Uma', 'Thurman', 'April 29, 1970');
const john = createPerson('John', 'Malkovich', 'December 9, 1953');
// a person has only three properties
console.log(uma);  // { firstname: 'Uma', lastname: 'Thurman', birthdate: 'April 29, 1970' }
console.log(john); // { firstname: 'John', lastname: 'Malkovich', birthdate: 'December 9, 1953' }
// the age method in the prototype object 
console.log(Object.getPrototypeOf(uma)); // { age: [Function: age] }
// invoke the age method in the shared prototype object
console.log(`${john.firstname} ${john.lastname} is ${john.age()}`); // John Malkovich is 66

All instances of the Person class share the same prototype object

  • the [[Prototype]] properties of uma and john objects points to the same personPrototype object

2 Classes and Constructors

What is a constructor function?

  • a constructor is a function designed for the initialization of newly created objects
  • by convention, a constructor function has its name written in uppercase
  • invoke a constructor using the new keyword:
    • for example: const john = new Person()
    • the new keyword automatically creates a new object
    • the constructor only has to initialize the state of the new object

Setting prototype for new objects

  • a constructor invocation automaticaly sets a prototype
    • the Person.prototype property is used to set the [[Prototype]] property of the new object
    • objects created by the same constructor inherits from the same prototype and therefore are instances of the same class
  • The prototype object of the constructor is stored as a property of the function object
    • the constructor function is an object and therefore it can have properties
    • the Person.prototype is a property of the Person function and you can add more functions to it
      • for example: you can add the age() method to Person.prototype

What happens when you call the Person constructor:

    const uma = new Person('Uma', 'Thurman', 'April 29, 1970');
  1. the new keyword creates a new blank object
  2. the Person function is invoked as method of new object
    • the this value is set to the new object
  3. the body of the Person function is executed to initialize the new object
    • the firstname, lastname, birthdate properties of the object are set to the constructor's arguments
  4. the [[Prototype]] property of the new object is set to the prototype object of the Person constructor function
  5. the newly constructed object is returned

Example: declaring the Person constructor function

// the Person constructor function
function Person(firstname, lastname, birthdate) {
    this.firstname = firstname;
    this.lastname = lastname;
    this.birthdate = birthdate;
}

// Add methods to the prototype of the Person constructor function
Person.prototype.age = function() {
    let millisecs = (new Date() - new Date(this.birthdate));
    return Math.floor( millisecs / (1000*60*60*24*365) );
}

Example: using the Person constructor function

// construct two instances
const uma  = new Person('Uma', 'Thurman', 'April 29, 1970');
const john = new Person('John', 'Malkovich', 'December 9, 1953');
// log the two instances of Person class
console.log(uma);  // Person { firstname: 'Uma', lastname: 'Thurman', birthdate: 'April 29, 1970' }
console.log(john); // Person { firstname: 'John', lastname: 'Malkovich', birthdate: 'December 9, 1953' }
// invoke age method from Person.prototype
console.log(`${john.firstname} ${john.lastname} is ${john.age()}`); // John Malkovich is 66
// invoke toString method from Object.Prototype
console.log(john.toString()); // [object Object]
// the prototype is an object with a single property, the age function
console.log(Person.prototype); // Person { age: [Function] }

In the image below, two objects were created by using the Person constructor function.

  • uma and john instances share the same prototype
  • Person.prototype has a [[Prototype]] property that points to Object.prototype
two instance of the Person class

Compare and contrast createPerson factory function and Person constructor function

  • constructor functions use capital letters whereas regular functions and methods begin with lowercase letters
  • the constructor is invoked with the new keyword, whereas the factory function without the new keyword
  • the constructors does not have to return the new object, whereas the factory function does
  • in the factory function, the prototype was arbitrary named personPrototype, whereas, in the constructor function, the prototype is named Person.prototype and this name is mandatory

Classes and arrow functions

  • both factory and constructor examples use ordinary functions, not arrow functions to define constructor; arrow functions do not have a prototype property and cannot be used as constructors
  • both the two examples use ordinary functions, not arrow functions, to define methods. Methods use this to refer to the object on which they are invoked. Ordinary functions set the this value based on the objects which they are invoked on, whereas arrow functions inherit this from the context where they are defined.

Prototypes as Class identity

  • the prototype object, not the constructor, is fundamental to the identity of a class
  • if two constructors have prototype properties that point to the same prototype object, then both the two constructors can be used to create instances of the same class.

Constructors as Class face

  • the constructor function represents the face of a class
    • the name of the constructor is adopted as the name of the class
    • we say that the Employee constructor creates Employee objects

How to test objects for membership in a class

  • use the constructor with the instanceof operator for testing objects for membership in a class
  • the obj instanceof Constructor expression evaluates to true if obj inherits from Constructor.prototype
  • let john = new Person();
    john instanceof Person 
    // -> true: john inherits from Person.prototype 
    
  • note: the inheritance need not to be direct: if obj inherits from obj2 and obj2 inherits from Constructor.prototype, then the expression still evaluates to true
  • note: the obj instanceof Constructor expression does not check whether obj is initialized by the Constructor function. If you create a People constructor function that sets the prototype to the same Person.prototype, then the instanceof operator will find objects created by People as instances to the same Person class

Testing for the prototype without using constructor function

  • the Object.prototype.isPrototypeOf() method allows to test the prototype chain without using the constructor
  • this method is useful if you defined a class using a factory function not a constructor function
  • proto.isPrototypeOf(obj) returns true if proto is in the prototype chain of obj
  • example: personPrototype.isPrototypeOf(person);

The constructor property

  • Any regular JavaScript function can be used as constructor
  • every regular JavaScript function has a prototype property, which points to the prototype object
  • the prototype object has a single property: the constructor property, which references to the function object:

The constructor property for class instances

  • All the functions has their predefined prototype objects that has its constructor property
  • every class instance inherits the constructor property that refers to its constructor function
  • the constructor serves as face of a class, the constructor property gives the class of the object
  • const john = new Person();
    john.constructor === Person; // true
    

3 Classes with the class Keyword

No comments:

Post a Comment