JavaScript Object-Oriented Programming

Introduction

Object-Oriented Programming with JavaScript

Contents

  1. Object-Oriented Programming in JavaScript
  2. Constructor Function in JavaScript
  3. ES6 class in javaScript
  4. Binding this in JavaScript
  5. Prototypal-based Inheritance in JavaScript
  6. Private class fields

1. Object-Oriented Programming in JavaScript

An Object is a box containing information and functions.

Object-Oriented Programming is a way of writing code that allows you to create different objects from a common object. Also, Object-Oriented Programming has been around since the 70s and It is a style of programming that is very common in languages such as C#, Python, Ruby, and Java.

JavaScript Object

let tim = {
    name: "Tim",
    country: "USA",
    speak(language) {
        if (language.toLowerCase() == "english") {
            return true;
        }
        return false;
    }
}

console.log(tim.country);
console.log(tim.speak("english"));
USA
true

the tim object have some Object-Oriented Principles.

tim object contains name, country and a speak function.

In comes to Object-Oriented Programming there are two main types there is class-based programming languages and prototype-based programming languages. JavaScript is a prototype-based programming language.

Object-Oriented Programming: Organize your code.

Basic

const tim = {
    name: "Tim",
    country: "USA",
    language: "english",
    speak() {
        return tim.name + " can speak " + tim.language
    }
}

const mike = {
    name: "Mike",
    country: "Spain",
    language: ["english", "spanish"],
    speak() {
        return mike.name + " can speak " + mike.language.toString();
    }
}

console.log(tim.speak())
console.log(mike.speak())
Tim can speak english
Mike can speak english,spanish

Object-Oriented Programming what we have something called Encapsulation. tim and mike are containers. That is good. However, if we have more people we need more and more objects. We should have to copy and paste the same code over and over. That is not our code very dry.

Sheared Object.

function createPeople(name, country, language){
    return {
        name: name,
        country: country,
        language: language,
        speak(){
            return name + " can speak " + language;
        }
    }
}

const tim = createPeople("Tim", "USA", "english")
const mike = createPeople("Mike", "Spain", ["english", "spanish"])

console.log(tim.speak());
console.log(mike.speak());
Tim can speak english
Mike can speak english,spanish

We have created a factory function that is a function that creates an object for us. We avoided repetitive code. However, Still, there is a problem. What if we had a thousand people which means a thousand people require our memory to store the same data. Things like name, country, and language are different but speak() function is pretty generic. So what we can do is

Shared Object with Share Function

function createPeople(name, country, language) {
    return {
        name: name,
        country: country,
        language: language,
    }
}

const speakFunction = {
    speak() {
        return this.name + " can speak " + this.language;
    }
}

const tim = createPeople("Tim", "USA", "english")
tim.speak = speakFunction.speak

const mike = createPeople("Mike", "Spain", ["english", "spanish"])
mike.speak = speakFunction.speak

console.log(tim.speak());
console.log(mike.speak());
Tim can speak english
Mike can speak english,spanish

JavaScript has a prototypal Inheritance. We can use that to our advantage to maybe improve with sharing functionality across different objects.

BUT we have shared functionality, it is still a lot of manual work.

Object.create()

function createPeople(name, country, language) {
    let newPeople = Object.create(speakFunction);
    newPeople.name = name;
    newPeople.country = country;
    newPeople.language = language;
    return newPeople;
}

const speakFunction = {
    speak() {
        return this.name + " can speak " + this.language;
    }
}

const tim = createPeople("Tim", "USA", "english")
const mike = createPeople("Mike", "Spain", ["english", "Spanish"])

console.log(tim);
console.log(tim.speak());
console.log(mike.speak());
console.log(tim.__proto__);
console.log(tim.__proto__.__proto__);
jayjo@Jay-Mac algorithms-2020 $ node j.js 
{ name: 'Tim', country: 'USA', language: 'english' }
Tim can speak english
Mike can speak english,Spanish
{ speak: [Function: speak] }
{}

What is Object.create()?

it creates a link between the speakFunction and newPeople. This is a prototypal Inheritance. However, some people do not like Object.create() style.

If you do not like this style, you can consider using class keyword in Java.

Most of the JavaScript community does not use Object.create() as much as class syntax.

2. Constructor Function in JavaScript

Any functions in JavaScript that is invoked by the new keyword is called a constructor function.

function People(name, country, language){
    this.name = name;
    this.country = country;
    this.language = language;
}

const tim = new People("Tim", "USA", "english")

console.log(tim.country);

Naming Convention: constructor function start with Capital letter. People

new keyword automatically returns the object for us it create the People constructor.

USA

By prototype, you can add more functionality.

function People(name, country, language) {
    this.name = name;
    this.country = country;
    this.language = language;
}

People.prototype.speak = function() {
    return this.name + " can speak " + this.language;
}

const tim = new People("Tim", "USA", "english");

tim.email = "tim@email.com";
console.log(tim.speak());
console.log(tim)
console.log(tim.__proto__);
console.log(tim.__proto__.__proto__);
Tim can speak english
People {
  name: 'Tim',
  country: 'USA',
  language: 'english',
  email: 'tim@email.com'
}
People { speak: [Function] }
{}

Again, as a rule all constructor functions should start with a capital letter to let other programmers know that you need to call this function using the new keyword.

You can also create People constructor with Native Function in JavaScript

const People = new Function("name", "country", "language", "this.name = name; this.country = country; this.language = language;");
const tim = new People("Tim", "USA", "english");
console.log(tim.country);
  • Last parameter is what the function is going to be doing.
  • Mostly, you are not going to use.
USA

Problem of function inside function with This Keyword

function People(name, country, language) {
    this.name = name;
    this.country = country;
    this.language = language;
}

People.prototype.speak = function () {
    function insideFunction() {
        return this.name + " can speak " + this.language;
    }
    return { insideFunction: insideFunction }
}

const tim = new People("Tim", "USA", "english");
console.log(tim.speak().insideFunction());
undefined can speak undefined

* this.name and this.language are undefined.

You can use solve with bind keyword or assign another variable.

Bind Keyword

function People(name, country, language) {
    this.name = name;
    this.country = country;
    this.language = language;
}

People.prototype.speak = function () {
    function insideFunction() {
        return this.name + " can speak " + this.language;
    }
    return { insideFunction: insideFunction.bind(this) }
}

const tim = new People("Tim", "USA", "english");
console.log(tim.speak().insideFunction());
Tim can speak english

assign another variable.

function People(name, country, language) {
    this.name = name;
    this.country = country;
    this.language = language;
}

People.prototype.speak = function () {
    const self = this;
    function insideFunction() {
        return self.name + " can speak " + self.language;
    }
    return { insideFunction: insideFunction }
}

const tim = new People("Tim", "USA", "english");
console.log(tim.speak().insideFunction());
Tim can speak english

Old code bases you might see a lot of prototype. This code there is no classes in here there is this weird thing where have to make sure to remember to have a Capital letter. That is why Object.create() was added the language in order to avoid the headache and just use pure prototypal Inheritance. The style of coding the idea of this code with new keyword is very much object-oriented programming is part especially when it comes to languages like Java.

Object.create() is technically less Object-Oriented than something like People.prototype.speak.

However, after ES6 JavaScript finally got the class keyword.

3. ES6 class in javaScript

Object-Oriented Programming was created with the class idea, and a class is a blueprint that defines the variables and the methods common to all objects of a certain kind.

With ECMAScript 6, JavaScript has the class keyword. What is ECMAScript? It is a JavaScript standard meant to ensure the interoperability of Web pages across different Web browsers.

class People {
    constructor(name, country, language) {
        this.name = name;
        this.country = country;
        this.language = language;
    }
    speak() {
        return this.name + " can speak " + this.language;
    }
}

const tim = new People("Tim", "USA", "english");
console.log(tim);
console.log(tim.speak());
console.log(tim.__proto__);
console.log(tim.__proto__.__proto__);
People { name: 'Tim', country: 'USA', language: 'english' }
Tim can speak english
People {}
{}

The beauty with classes is that as Object-Oriented programming suggests we want to keep all our functionalities and all the state inside of the box. Class is a nice contained environment.

In JavaScript, the language is still using prototypal inheritance. JavaScript is not using classes like classes work in other languages. JavaScript was competing with Java for marketing purposes they could not make it the exact same. So, Brendan Eich had to be creative. So, he used prototypal Inheritance which is quite different from how classes work in Languages like Java and c++. In other languages classes are an actual thing, but JavaScript classes are still just object.

You can say JavaScript has classes, but class keyword is still just prototypal inheritance.

Also, const tim = new People("Tim", "USA", "english"); if you use new keyword, it instantiate a class.

Methods in Class

speak() {
    return this.name + " can speak " + this.language;
}

that is going to take up memory space. Instead of creating an speak function from each object. you can have on function in one location that all the instances of class can access.

Object.create() vs Class

There is a big debate in the JavaScript Community. People that love classes and people that never want to use new and this keyword. This is just a personal preference. With Object.create(), we are able to create prototypal inheritance without pretending like we have classes. However, most of the JavaScript community does not use Object.create() as much as class.

4. Binding this in JavaScript

This is dynamically scoped that is it gets determined where it is called.

In most cases, the value of this is determined by how a function is called (runtime binding). It can’t be set by assignment during execution, and it may be different each time the function is called.

There are 4 ways to use this.

  • Default and Implicit binding this.
  • Explicit binding this.
  • With arrow function binding this
  • With new keyword binding this.
  1. Default and Implicit binding this.

Create object and inside of the object the this keyword will refer to person.

const tim = {
    name: "Tim",
    country: "USA",
    language: "english",
    speak() {
        return this.name + " can speak " + this.language;
    }
}

console.log(tim.speak())
Tim can speak english

2. Explicit binding this.

const mike = {
    name: "Mike",
    country: "Spain",
    language: ["english", "spanish"],
    speak() {
        function innerFunction() {
            return (this.name + " can speak " + this.language);
        }
        return innerFunction.bind(this)
    }
}

console.log(mike.speak()())
Mike can speak english,spanish

3. With arrow function binding this

implicit, and explicit binding This is dynamically scoped that is it get determined where it is called. With arrow functions you can use this like lexically scoped.

const mike = {
    name: "Mike",
    country: "Spain",
    language: ["english", "spanish"],
    speak() {
        const innerFunction = () =>{
            return (this.name + " can speak " + this.language);
        }
        return innerFunction()
    }
}

console.log(mike.speak())
Mike can speak english,spanish

4. With new keyword binding this.

function People(name, country, language){
    this.name = name;
    this.country = country;
    this.language = language;
}

const tim = new People("Tim", "USA", "english")

console.log(tim.country);
USA

5. Prototypal-based Inheritance in JavaScript

In JavaScript, inheritance is by a prototype object. it calls “Prototypal-based Inheritance”.

In object-oriented programming, inheritance is the mechanism of basing an object or class upon another object (prototype-based inheritance) or class (class-based inheritance), retaining similar implementation. 

class Person {
    constructor(name, country, language) {
        this.name = name;
        this.country = country;
        this.language = language;
    }
    speak() {
        return this.name + " can speak " + this.language;
    }
}

class American extends Person{
    constructor (name, country, language, location){
        super(name, country, language);
        this.location = location;
    }
}

const tim = new American("tim", "USA", "english", "North");

console.log(tim.speak());
console.log(tim.location);
console.log(tim.__proto__);
console.log(tim.__proto__.__proto__);
console.log(tim.__proto__.__proto__.__proto__);
tim can speak english
North
American {}
Person {}
{}

American class is sub class and Person is base class, and sub-class is inherited from the base class’s all the properties and methods.

When we do class American extends Person Class, it means to extend and set the prototype that is __.proto__ to point the Person. So, anytime you create an instance of American like tim and tim can use all the properties and methods in the Person class. However, if you want to get any parameters from based class, you need super keyword. super keyword is used to access and call functions on an object’s parent.

this without super keyword

class American extends Person{
    constructor (name, country, language, location){
        console.log(this);
        super(name, country, language);
        this.location = location;
    }
}
const tim = new American("tim", "USA", "english", "North");
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
  • you will get referenceError withoud super keyword.

this with super keyword


class American extends Person{
    constructor (name, country, language, location){
        super(name, country, language);
        console.log(this);
        this.location = location;
        console.log(this);
    }
}

const tim = new American("tim", "USA", "english", "North");
American { name: 'tim', country: 'USA', language: 'english' }
American {
  name: 'tim',
  country: 'USA',
  language: 'english',
  location: 'North'
}

Sub-class has methods

class Person {
    constructor(name, country, language) {
        this.name = name;
        this.country = country;
        this.language = language;
    }
    speak() {
        return this.name + " can speak " + this.language;
    }
}

class American extends Person{
    constructor (name, country, language, location){
        super(name, country, language);
        this.location = location;
    }
    think(){
        return "Think by " + this.language;
    }

}

const tim = new American("tim", "USA", "english", "North");

console.log(tim.speak());
console.log(tim.think());
tim can speak english
Think by english

This is a way to keep our code nice and organized and using extends class.

Lastly, we look prototype for sub-class

class Person {
    constructor(name, country, language) {
        this.name = name;
        this.country = country;
        this.language = language;
    }
    speak() {
        return this.name + " can speak " + this.language;
    }
}

class American extends Person{
    constructor (name, country, language, location){
        super(name, country, language);
        this.location = location;
    }
    think(){
        return "Think by " + this.language
    }

}

const tim = new American("tim", "USA", "english", "North");

console.log(American.prototype.speak());
console.log(American.prototype.think());
console.log(American.prototype.isPrototypeOf(tim));
console.log(Person.prototype.isPrototypeOf(tim));
console.log(tim instanceof American);
console.log(tim instanceof Person);
undefined can speak undefined
Think by undefined
true
true
true
true

The inheritance which is what we do with the keyword extends is inheriting from a base class or a higher class. Inheritance in JavaScript does not copy our functionality and properties. Instead, it simply links up the prototype chain. So, it is creating an efficient linking in JavaScript using prototypal inheritance. The interesting thing is that language like Java and C++ actually copy objects. when we do like extend instead of what we do with JavaScript which is that we link and the object is referenced there is actually a bit of efficiency there in terms of memory.

6. Private class fields

In many Object-Oriented programming languages that have classes the idea of private and public fields is really important. Now, JavaScript does not have that. Other languages like Java, C# actually have keywords like private that make things private for us they are methods that is only used inside of the the class.

 however an experimental proposal to allow defining private class fields using a hash # prefix is added.

#name = "Mike"

the name is now private data that can be accessed inside of the class.

However, properties and methods can not be private at least for now.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields

Leave a Reply

Your email address will not be published.

ANOTE.DEV