JS Oop
JS Oop
Introduction to the factory functions in JavaScript - A factory function is a function that returns a new object. The following
creates a person object named john:
let john = {
firstName: 'John',
lastName: 'Doe',
getFullName() {
return this.firstName + ' ' + this.lastName;
}
};
console.log(john.getFullName());
The john object has two properties: firstName and lastName, and one method getFullName() that returns the full name. Suppose
that you need to create another similar object called jane, you can duplicate the code as follows:
let jane = {
firstName: 'Jane',
lastName: 'Doe',
getFullName() {
return this.firstName + ' ' + this.lastName;
}
};
console.log(jane.getFullName());
The jane object has the same properties and method as the john object. The more object you want to create, the more duplicate
code you need to copy. To avoid copying the same code all over again and again, you can develop a function that creates
the person object:
console.log(john.getFullName());
console.log(jane.getFullName());
With the factory function, you create any number of the person objects you want without duplicating code.
When you create an object, that object requires a space in the memory. If you have a thousand person objects, you need one
thousand spaces in the memory to store these objects. These person objects, however, have the same getFullName() method.
To avoid repeating the same getFullName() function in the memory, you can remove the getFullName() method from
the person object:
const behavior = {
getFullName() {
return this.firstName + ' ' + this.lastName;
}
}
And before calling the getFullName() method on the person object, you can assign the method of the behavior object to
the person object as follows:
const behavior = {
getFullName() {
return this.firstName + ' ' + this.lastName;
}
}
console.log(john.getFullName());
console.log(jane.getFullName());
The code works perfectly fine. However, in practice, you will rarely see the factory functions. Instead, you will see the function
constructors or the classes.
Example - Factory functions do not require the use of the new keyword, but can still be used to initialize an object, like a
constructor. Often, factory functions are used as API wrappers, like in the cases of jQuery and moment.js, so users do not need to
use new. The following is the simplest form of factory function; taking arguments and using them to craft a new object with the
object literal:
function cowFactory(name) {
return {
name: name,
talk: function () {
console.log('Moo, my name is ' + this.name);
},
};
}
var daisy = cowFactory('Daisy'); // create a cow named Daisy
daisy.talk(); // "Moo, my name is Daisy"
It is easy to define private properties and methods in a factory, by including them outside of the returned object. This keeps your
implementation details encapsulated, so you can only expose the public interface to your object.
function cowFactory(name) {
function formalName() {
return name + ' the cow';
}
return {
talk: function () {
console.log('Moo, my name is ' + formalName());
},
};
}
var daisy = cowFactory('Daisy');
daisy.talk(); // "Moo, my name is Daisy the cow"
daisy.formalName(); // ERROR: daisy.formalName is not a function
The last line will give an error because the function formalName is closed inside the cowFactory function. This is a closure.
Factories are also a great way of applying functional programming practices in JavaScript, because they are functions.
In this JavaScript example the Factory object creates four different types of employees. Each employee type has a different
hourly rate. The createEmployee method is the actual Factory Method. The client instructs the factory what type of employee to
create by passing a type argument into the Factory Method.
The AbstractProduct in the diagram is not implemented because Javascript does not support abstract classes or interfaces.
However, we still need to ensure that all employee types have the same interface (properties and methods).
Four different employee types are created; all are stored in the same array. Each employee is asked to say what they are and their
hourly rate. The log function is a helper which collects and displays results.
function Factory() {
this.createEmployee = function (type) {
var employee;
if (type === "fulltime") {
employee = new FullTime();
} else if (type === "parttime") {
employee = new PartTime();
} else if (type === "temporary") {
employee = new Temporary();
} else if (type === "contractor") {
employee = new Contractor();
}
employee.type = type;
employee.say = function () {
log.add(this.type + ": rate " + this.hourly + "/hour");
}
return employee;
}
}
// log helper
var log = (function () {
var log = "";
return {
add: function (msg) { log += msg + "\n"; },
show: function () { alert(log); log = ""; }
}
})();
function run() {
var employees = [];
var factory = new Factory();
employees.push(factory.createEmployee("fulltime"));
employees.push(factory.createEmployee("parttime"));
employees.push(factory.createEmployee("temporary"));
employees.push(factory.createEmployee("contractor"));
A factory function is any function which is not a class or constructor that returns a (presumably new) object. In JavaScript, any
function can return an object. When it does so without the new keyword, it‘s a factory function.
Factory functions have always been attractive in JavaScript because they offer the ability to easily produce object instances
without diving into the complexities of classes and the new keyword.
JavaScript provides a very handy object literal syntax. It looks something like this:
const user = {
userName: 'echo',
avatar: 'echo.png'
};
Like JSON (which is based on JavaScript‘s object literal notation), the left side of the : is the property name, and the right side is
the value. You can access props with dot notation:
console.log(user.userName); // "echo"
You can access computed property names using square bracket notation:
If you have variables in-scope with the same name as your intended property names, you can omit the colon and the value in the
object literal creation:
In this case, user.setUserName('Foo') applies .setUserName() to user, so this === user. In the .setUserName() method, we change
the .userName property on the user object via its this binding, and return the same object instance for method chaining.
If you need to create many objects, you‘ll want to combine the power of object literals and factory functions.
With a factory function, you can create as many user objects as you want. If you‘re building a chat app, for instance, you can
have a user object representing the current user, and also a lot of other user objects representing all the other users who are
currently signed in and chatting, so you can display their names and avatars, too.
Returning Objects
Arrow functions (=>) have an implicit return feature: if the function body consists of a single expression, you can omit
the return keyword: () => 'foo' is a function that takes no parameters, and returns the string, "foo".
Be careful when you return object literals. By default, JavaScript assumes you want to create a function body when you use
braces, e.g., { broken: true }. If you want to use an implicit return for an object literal, you‘ll need to disambiguate by wrapping
the object literal in parentheses:
In the first example, foo: is interpreted as a label, and bar is interpreted as an expression that doesn‘t get assigned or returned.
The function returns undefined.
In the createFoo() example, the parentheses force the braces to be interpreted as an expression to be evaluated, rather than a
function body block.
Destructuring
And you can use the rest and spread syntax (...varName) to gather the rest of the values from the array (or a list of arguments),
and then spread those array elements back into individual elements:
Earlier we used square bracket computed property access notation to dynamically determine which object property to access:
In this example, arrToObj takes an array consisting of a key/value pair (aka a tuple) and converts it into an object. Since we don‘t
know the name of the key, we need to compute the property name in order to set the key/value pair on the object. For that, we
borrow the idea of square bracket notation from computed property accessors, and reuse it in the context of building an object
literal:
{ [key]: value }
{ "foo": "bar" }
Default Parameters
Functions in JavaScript support default parameter values, which have several benefits:
Using default parameters, we can document the expected interface for our createUser factory, and automatically fill
in 'Anonymous' details if the user‘s info is not supplied:
const createUser = ({
userName = 'Anonymous',
avatar = 'anon.png'
} = {}) => ({
userName,
avatar
});
console.log(
// { userName: "echo", avatar: 'anon.png' }
createUser({ userName: 'echo' }),
The last part of the function signature probably looks a little funny:
} = {}) => ({
The last = {} bit just before the parameter signature closes means that if nothing gets passed in for this parameter, we‘re going
to use an empty object as the default. When you try to destructure values from the empty object, the default values for
properties will get used automatically, because that‘s what default values do: replace undefined with some predefined value.
Without the = {} default value, createUser() with no arguments would throw an error because you can‘t try to access properties
from undefined.
2. The Object.create() method
The Object.create() method creates a new object, using an existing object as the prototype of the newly created object.
const person = {
isHuman: false,
printIntroduction: function() {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`);
}
};
const me = Object.create(person);
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
This pattern comes in very handy when we are asked to create objects from other existing objects and not directly using the
‗new‘ keyword syntax. Let‘s see how to use this pattern. As stated on MDN:
The Object.create() method creates a new object, using an existing object as the prototype of the newly created object.
Parameters Used:
prototype : It is the prototype object from which a new object has to be created.
propertiesObject : It is optional parameter. It specifies the enumerable properties to be added to the newly created object.
Return Value: Object.create() returns a new object with the specified prototype object and properties.
Example:
const person = {
name: 'Full Name',
email: '[email protected]',
};
console.log(pankaj); // → {}
console.log(pankaj.name); // → 'Full Name'
pankaj.name = 'Pankaj';
console.log(pankaj); // → { name: 'Pankaj' }
console.log(pankaj.name); // → 'Pankaj'
console.log(pankaj.__proto__.name); // → 'Fullest Name'
And what if you wanna add some new properties to the object while creating the new object. This example shows us that:
const person = {
name: 'Full Name',
email: '[email protected]',
};
As opposed to object literals, here, you define an object type without any specific values. Then, you create new object instances
and populate each of them with different values.
Below, you can see the same user001 object defined by using a constructor function called function User(). The constructor
creates an object type called User(). Then, we create a new object instance called user001, using the new operator. The
constructor function contains three this statements that define the three properties with empty values. The values of the
properties are added by each object instance.
The console returns the user001 object the same way as before. However, this time it‘s the instance of the custom User() object
type instead of the pre-built Object(). This is the main thing in which object literals and objects created with constructors are
different from each other.
console.log(user001);
// User {firstName: "John", lastName: "Smith", dateOfBirth: 1985}
Besides properties, you can also define methods within a constructor function. You need to use almost the same syntax as with
methods created for object literals. The only difference is that here, you also need to add the this keyword before the name of
the method.
this.getName = function(){
return "User's name: " + this.firstName + " " + this.lastName;
}
}
var user001 = new User("John", "Smith", 1985);
When you test the method in the console, it returns the same result as before. Here, also don‘t forget to put parentheses after
the method‘s name.
console.log(user001.getName());
// User's name: John Smith
class Person {
constructor(fname, lname) {
this.firstName = fname;
this.lastName = lname;
}
}
const person = new Person('testFirstName', 'testLastName');
console.log(person.firstName); // testFirstName
console.log(person.lastName); // testLastName
OOP in JavaScript
The two important principles with OOP in JavaScript are Object Creation patterns (Encapsulation) and Code Reuse patterns
(Inheritance). When building applications, you create many objects, and there exist many ways for creating these objects: you
can use the ubiquitous object literal pattern, for example:
function Employee () {}
Employee.prototype.firstName = "Abhijit";
Employee.prototype.lastName = "Patel";
Employee.prototype.startDate = new Date();
Employee.prototype.signedNDA = true;
Employee.prototype.fullName = function () {
console.log (this.firstName + " " + this.lastName);
};
You can also use the constructor pattern, a constructor function (Classes in other languages, but Functions in JavaScript). For
example:
var richard = new Employee (“Richard”, “Developer”) // richard is a new object we create from the
Employee () constructor function.
console.log(richard.name); //richard
console.log(richard.profession); // Developer
In the latter example, we use a custom constructor function to create an object. This is how we create objects when we want to
add methods and properties on our objects, and when we want to encapsulate functionality on our objects. JavaScript
developers have invented many patterns (or ways) for creating objects with constructor functions. And when we say Object
Creation Patterns, we are concerned principally with the many ways of creating objects from constructor functions, as in the
preceding example.
In addition to the patterns for creating objects, you want to reuse code efficiently. When you create your objects, you will likely
want some of them to inherit (have similar functionality) methods and properties from a parent object, yet they should also have
their own methods and properties. Code reuse patterns facilitate ways in which we can implement inheritance.
These two universal principles—creating objects (especially from constructor Functions) and allowing objects to inherit
properties and methods—are the main focus of this article and, indeed, the main concepts with OOP in JavaScript. We first
discuss the object creation pattern.
As JavaScript is widely used in Web Development, in this article we would explore some of the Object Oriented mechanism
supported by JavaScript to get most out of it. Some of the common interview question in JavaScript on OOPS includes, - ―How
Object-Oriented Programming is implemented in JavaScript? How they differ from other languages? Can you implement
Inheritance in JavaScript and so on…?‖
There are certain features or mechanisms which makes a Language Object Oriented like:
Let‘s dive into the details of each one of them and see how they are implemented in JavaScript.
Object – An Object is a unique entity which contains property and methods. For example ―car‖ is a real life Object, which have
some characteristics like color, type, model, horsepower and performs certain action like drive. The characteristics of an Object
are called as Property, in Object Oriented Programming and the actions are called methods. An Object is an instance of a class.
Objects are everywhere in JavaScript almost every element is an Object whether it is a function, arrays and string.
Note: A Method in JavaScript is a property of an object whose value is a function. Object can be created in two ways in
JavaScript:
Object literal - Create a new object in JavaScript by setting its properties inside curly braces. Object literals property values can
be any data types, like function literals, arrays, strings, numbers, or Boolean. Let‘s create an object with a named book, with
properties such as author, published year, title, and a method — summary.
const book = {
title: "Hippie",
author: "Paulo Coelho",
year: "2018"
}
After creating an object you can get the value with dot notation. For example, we can get the value of the title
with book.title. We can also access the properties with square brackets: book[‗title‘].
Object constructor - Object constructor is the same as a regular function. It will be called each time an object is created. We can
use them with the new keyword. Object constructor is useful when we want to create multiple objects with the same properties
and methods.
const book = {
title: "Hippie",
author: "Paulo Coelho",
year: "2018"
}
const book1 = {
title: "The Alchemist",
author: "Paulo Coelho",
year: "1988",
}
If we want to create multiple book objects we have to duplicate the code for each book. We can keep creating books but it‘s
kind of a pain — the object constructor helps us to reuse the object literal.
> Book {
title: "Hippie",
author: "Paulo Coelho",
year: "2018"
}
// if we want to create more than one book just we call function book with new keyword.const book2 =
new Book ('The Alchemist', 'Paulo Coelho', '1988');
book1 and book2 create an instance of Book and assigned it to a variable. To find out whether an object is an instance of
another one. We can use instanceof.
book1 instanceof Book
> true
Object.create() - Every object in JavaScript will create from the master Object. Whenever we use object with a capital ‗O‘ it refers
to the master object. We can print the master object in the console. The master object has the number of methods and here we
are going to see Object.create() method.
The Object.create() method creates a new object, using an existing object as the prototype. Here‘s the basic syntax:
Object.create(proto, [propertiesObject])
const Book = {
summary : function() {
console.log(`${this.title} is written by ${this.author}.`)
}
}
const book1 = Object.create(Book);
book1.author = "Paulo Coelho";
book1.title = "Hippie";
console.log(book1.summary());
> Hippie is written by Paulo Coelho.
//Defining object
let person = {
first_name:'Mukul',
last_name: 'Latiyan',
//method
getFunction : function(){
return (`The name of the person is
${person.first_name} ${person.last_name}`)
},
//object within object
phone_number : {
mobile:'12345',
landline:'6789'
}
}
console.log(person.getFunction());
console.log(person.phone_number.landline);
Output:
//using a constructor
function person(first_name,last_name){
this.first_name = first_name;
this.last_name = last_name;
}
//creating new instances of person object
let person1 = new person('Mukul','Latiyan');
let person2 = new person('Rahul','Avasthi');
console.log(person1.first_name);
console.log(`${person2.first_name} ${person2.last_name}`);
Output:
Using Object.create() method: The Object.create() method creates a new object, using an existing object as the
prototype of the newly created object.
// Object.create() example a
// simple object with some properties
const coder = {
isStudying : false,
printIntroduction : function(){
console.log(`My name is ${this.name}. Am I
studying?: ${this.isStudying}.`)
}
}
// Object.create() method
const me = Object.create(coder);
me.printIntroduction();
Output:
Classes – Classes are blueprint of an Object. A class can have many Object, because class is a template while Object
are instances of the class or the concrete implementation.
Before we move further into implementation, we should know unlike other Object Oriented Language there is no classes in
JavaScript we have only Object. To be more precise, JavaScript is a prototype based object oriented language, which means it
doesn‘t have classes rather it define behaviors using constructor function and then reuse it using the prototype.
Note: Even the classes provided by ECMA2015 are objects.
JavaScript classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript’s existing prototype-based
inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a
much simpler and clearer syntax to create objects and deal with inheritance. –Mozilla Developer Network
Class syntax is a nice way to use object-oriented programming and managing prototypes:
let Book = function(name) {
this.name = name
}
let newBook = function(name) {
Book.call(this, name)
}
newBook.prototype = Object.create(Book.prototype);
const book1 = new newBook("The Alchemist");
class Book {
constructor(name) {
this.name = name
}
}
class newBook extends Book {
constructor(name) {
super(name);
}
}
const book1 = new newBook("The Alchemist");
Class syntax is syntactical sugar — behind the scenes, it still uses a prototype-based model. Classes are functions, and functions
are objects in JavaScript.
class Book {
constructor(title, author){
this.title = title;
this.author = author;
}
summary() {
console.log(`${this.title} written by ${this.author}`);
}
}
const book1 = new Book("", "");
console.log(typeof Book);
> "function"console.log(typeof book1);
> "object"
Example:
Let‘s use ES6 classes then we will look into traditional way of defining Object and simulate them as classes.
console.log(bike1.name); // Hayabusa
console.log(bike2.maker); // Kawasaki
console.log(bike1.getDetails());
Output:
Traditional Way.
Vehicle.prototype.getDetails = function(){
console.log('The name of the bike is '+ this.name);
}
console.log(bike1.name);
console.log(bike2.maker);
console.log(bike1.getDetails());
Output:
As seen in the above example it is much simpler to define and reuse object in ES6. Hence, we would be using ES6 in all our
examples.
Encapsulation
The process of wrapping property and function within a single unit is known as encapsulation.
Encapsulation means hiding information or data. It refers to the ability of the object to execute its functionality without revealing
any execution details to the caller. In other words, the private variable is only visible to the current function and is not accessible
to the global scope or other functions.
return {
summary : function() {
console.log(`${title} written by ${author}.`);
}
}
}
const book1 = new Book('Hippie', 'Paulo Coelho');
book1.summary();
> Hippie written by Paulo Coelho.
In the above code the title and the author are only visible inside the scope of the function Book and the method summary is
visible to the caller of Book. So the title and the author are encapsulated inside Book.
//encapsulation example
class person{
constructor(name,id){
this.name = name;
this.id = id;
}
add_Address(add){
this.add = add;
}
getDetails(){
console.log(`Name is ${this.name},Address is: ${this.add}`);
}
}
Output:
In the above example we simply create a person Object using the constructor and Initialize it property and use it functions we are
not bother about the implementation details. We are working with an Objects interface without considering the implementation
details.
Sometimes encapsulation refers to hiding of data or data Abstraction which means representing essential features hiding the
background detail. Most of the OOP languages provide access modifiers to restrict the scope of a variable, but there are no such
access modifiers in JavaScript but there are certain way by which we can restrict the scope of variable within the Class/Object.
Example:
// Abstraction example
function person(fname,lname){
let firstname = fname;
let lastname = lname;
this.getDetails_access = function(){
return (`First name is: ${firstname}, Last
name is: ${lastname}`);
}
}
let person1 = new person('Mukul','Latiyan');
console.log(person1.firstname);
console.log(person1.getDetails_noaccess);
console.log(person1.getDetails_access());
Output:
In the above example we try to access some property(person1.firstname) and functions(person1.getDetails_noaccess) but it
returns undefined while there is a method which we can access from the person object(person1.getDetails_access()), by changing
the way to define a function we can restrict its scope.
Inheritance
So let‘s explore the ideas of inheritance, encapsulation, and polymorphism. We will make some animals—three different kinds of
animals. All animals will have a name, and they will all speak by saying their name and making a species-specific sound:
class Animal {
constructor(name) { this.name = name; }
speak() { return `${this.name} says ${this.sound()}`; }
}
class Cow extends Animal {
sound() { return 'moooo'; }
}
class Horse extends Animal {
sound() { return 'neigh'; }
}
class Sheep extends Animal {
sound() { return 'baaaa'; }
}
class Meetup {}
class TechMeet extends Meetup {}
class SportMeet extends Meetup {}
let js = new TechMeet();
console.log(js instanceof TechMeet); // true
console.log(js instanceof Meetup); // true
console.log(js instanceof Object); // true
The above code snippet is new way of achieving inheritance in JavaScript using extends and class keyword.
We can see that object js is instance of class TechMeet and Meetup both because class TechMeet extends parent
class Meetup.
Example of Inheritance with constructor
class Meetup {
constructor() {
console.log("inside Meetup constructor");
}
}
class TechMeet extends Meetup {
constructor() {
super();
console.log("inside TechMeet constructor");
}
}
let js = new TechMeet();
// inside Meetup constructor
// inside TechMeet constructor
Inside constructor function of child class TechMeet, we have to call super() method to call the parent constructor first
otherwise JavaScript will throw error.
super() call is must in constructor of derived class whether explicit presence of parent constructor exists or not.
class Meetup {
constructor(organizer) {
this.organizer = organizer;
}
}
We can pass the argument from child constructor to parent constructor through super() method like above code snippet.
We can override the parent class properties inside constructor of child class.
class Meetup {
organise() {
console.log('Organising Meetup');
} static getMeetupFounderDetails() {
console.log("Meetup Founder Details");
}
}
class TechMeet extends Meetup {
organise() {
//super.organise();
console.log('Organising TechMeet');
super.organise();
} static getMeetupFounderDetails() {
console.log("TechMeet Founder Details");
super.getMeetupFounderDetails();
}
}
/* Output:
Organising TechMeet
Organising Meetup
*/
TechMeet.getMeetupFounderDetails();
/*
Output:
TechMeet Founder Details
Meetup Founder Details
*/
Child class can access the methods of parent class using super object like super.organise().
Similarly static methods of child class can access the static method of parent class with help of super object.
Reusability/Inheritance - JavaScript inheritance is a mechanism allows us to create a new class using the existing class. It means
the child class inherits all the properties and behaviors of the parent class.
Generally, JavaScript is not a class-based language. The keyword class was introduced in ES6 but is syntactical sugar, JavaScript
remains prototype-based. In JavaScript inheritance is achieved by using the prototype. This pattern is called Behavior Delegation
Pattern or prototypal inheritance. Let‘s consider our book example:
It is a concept in which some property and methods of an Object is being used by another Object. Unlike most of the OOP
languages where classes inherit classes, JavaScript Object inherits Object i.e. certain features (property and methods) of one
object can be reused by other Objects. Let‘s understand inheritance with example:
//Inheritance example
class person{
constructor(name){
this.name = name;
}
//method to return the string
toString(){
return (`Name of person: ${this.name}`);
}
}
class student extends person{
constructor(name,id){
//super keyword to for calling above class constructor
super(name);
this.id = id;
}
toString(){
return (`${super.toString()},Student ID: ${this.id}`);
}
}
let student1 = new student('Mukul',22);
console.log(student1.toString());
Output:
In the above example we define a Person Object with certain property and method and then we inherit the Person Object in
the Student Object and use all the property and method of person Object as well define certain property and methods
for Student.
Note: The Person and Student object both have same method i.e. toString(), this is called as Method Overriding. Method
Overriding allows method in a child class to have the same name and method signature as that of a parent class.
In the above code, super keyword is used to refer immediate parent class instance variable.
Prototypal inheritance
For each instance of Book, we‘re recreating the memory for the methods from the base class. These methods must be shared
across all instances — they should not be specific to the instance. Here the prototype comes into the picture:
Corebook.prototype.Title = function() {
console.log(`name of the book is ${this.title}`);
}
Corebook.prototype.summary = function(author) {
console.log(`${this.title} is written by ${this.author}`);
}
Book.prototype = Object.create(Corebook.prototype);
let book1 = new Book('The Alchemist', 'Paulo Coelho');
book1.Title();
> name of the book is The Alchemist
book1.summary();
> The Alchemist is written by Paulo Coelho
In the above code, the instance of Book has a copy of the prototype, which is linked to the prototype of Book, which in turn links
to the prototype of Corebook.
Abstraction
Abstraction means implementation hiding. It is a way of hiding the implementation details and only showing the essential
features to the caller. In other words, it hides irrelevant details and shows only what‘s necessary to the outer world. A lack of
abstraction will lead to problems of code maintainability.
// Private method
const summary = function() {
return `${title} written by ${author}.`
}
book1.giveTitle();
> "Hippie"
book1.summary();
> Uncaught TypeError: book1.summary is not a functionbook1.giveSummary();
> "Hippie written by Paulo Coelho."
Polymorphism
The ability to call the same method on different objects and have each of them respond in their own way is called polymorphism.
Relationships between the objects will be defined by Association, Aggregation, and Composition.
Association - Association is the relationship between two or more objects. Each Object is independent. In other words,
association defines the multiplicity between objects: one-to-one, one-to-many, many-to-one, many-to-many.
book1 was assigned to the property of multiplicity to object book2. It shows the relationship between objects book1 and book2.
Both can be added and removed independently.
Aggregation - Aggregation is a special case of an association. In the relationship between two objects, one object can have a
more major role than the other. In other words, when an object takes more ownership than another one, that is aggregation. The
owner object is often called the aggregate and the owned object is called the component. Aggregation is also called a ―Has-a‖
relationship.
let publication = {
"name": "new publication Inc",
"books": []
}
publication.books.push(book1);
publication.books.push(book2);
book1 and book2 were added to books under publication object. If the publication object is deleted until book1 and book2 are
available, then both Book and publication live independently.
Composition - Composition is a special case of aggregation. Composition is when an object contains another object and the
contained object can‘t live without the container object.
let Book = {
"title": "The Alchemist",
"author": "Paulo Coelho",
"publication": {
"name": "new publication Inc",
"address": "chennai"
}
}
Here the property publication is strictly bounded with the Book object, and publication can‘t live without theBook object. If
the Book object id was deleted, then the publication would also be deleted.
Inheritance is when an object is based on another object. For example, book1 has inherited properties and methods for books
like title, author and summary. So it creates the relationship that book1 is-a Book.
The composition is collecting simple objects and combining them to build more complex objects. To build book1 we need
methods, like paper and a pen. So it creates a relationship that book1 has-a paper and a pen:
Object.assign() Method - Among the Object constructor methods, there is a method Object.assign() which is used to copy the
values and properties from one or more source objects to a target object. It invokes getters and setters since it uses both [[Get]]
on the source and [[Set]] on the target. It returns the target object which has properties and values copied from the target
object. Object.assign() does not throw on null or undefined source values.
Applications:
Parameters Used:
target : It is the target object from which values and properties have to be copied.
sources : It is the source object to which values and properties have to be copied.
Code 1:
<script>
<!-- creating an object constructor and assigning values to it -->
const obj1 = { a: 1 };
<!--creating a target object and copying values and properties to it using object.assign() method -->
const new_obj = Object.assign({}, obj1);
Code 2:
<script>
<!-- creating 3 object constructors and assigning values to it -->
var obj1 = { a: 10 };
var obj2 = { b: 20 };
var obj3 = { c: 30 };
<!--creating a target object and copying values and properties to it using object.assign() method -->
var new_obj = Object.assign({}, obj1, obj2, obj3);
Code 3:
<script>
<!--creating a target object and copying values and properties to it using object.assign() method -->
var new_obj = Object.assign({}, obj1, obj2, obj3);
Each instance of the class will have its own properties which gets created at constructor but Class can have also it‘s own
properties.
class Meetup {
constructor(name, location) {
this.name = name;
this.location = location;
} start() {
console.log(this.name + 'Meetup ' + 'is started at ' + this.location);
} static getAddress() {
console.log('Returned Address');
/* this.location will return undefined */
console.log('City: '+ this.location );
}
}
Meetup.admin = "Adam";
Meetup.getMembers = function () {
console.log(Meetup.admin+ ' Returned Members');
}
The instance jsMeetup cannot access the class only methods i.e. static methods getMembers() and getAddress(). Same with
static properties as well like admin is static property.
We can put label as static in-front of methods and properties inside the class definition to make it accessible to class only or
we can add it later to the class as well like Meetup.getMembers = function(){...}.
The scope of this keyword is current execution context of the method. In case of static method, the execution context can
never be class instance or object. That‘s the reason this.location of static method getAddress() prints undefined.
class Meetup {
constructor(name) {
this._name = name;
}
get name() {
// Validation can happen on data
return this._name;
}
set name(val) {
// Validation can happen on data
this._name = val;
}
}
let meetup = new Meetup('JS');
console.log("meetup Name: " + meetup.name); // meetup Name: JSmeetup.name = 'Angular';
console.log("meetup Name: " + meetup.name); // meetup Name: Angular
With getter and setter, will have more control on object properties after initialization with constructor.
We can do required validation on data within get and set method before setting or getting the value.
We can see in the above code that the instance property name is _name but we are using it as meetup.name and its working
fine because of getter and setter methods.
Prototypes
Using classes for inheritance is called classical inheritance. Using prototypes gives you prototypal inheritance. Let‘s try the
animals example with prototypes:
animals-prototypes.js
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return `${this.name} says ${this.sound()}`;
};
function Cow(name) {
Animal.call(this, name);
}
Object.setPrototypeOf(Cow.prototype, Animal.prototype);
Cow.prototype.sound = function () { return 'moooo'; };
function Horse(name) {
Animal.call(this, name);
}
Object.setPrototypeOf(Horse.prototype, Animal.prototype);
Horse.prototype.sound = function () { return 'neigh'; };
function Sheep(name) {
Animal.call(this, name);
}
Object.setPrototypeOf(Sheep.prototype, Animal.prototype);
Sheep.prototype.sound = function () { return 'baaaa'; };
User.prototype = {
constructor: User,
saveScore:function (theScoreToAdd) {
this.quizScores.push(theScoreToAdd)
},
showNameAndScores:function () {
var scores = this.quizScores.length > 0 ? this.quizScores.join(",") : "No Scores Yet";
return this.name + " Scores: " + scores;
},
changeEmail:function (newEmail) {
this.email = newEmail;
return "New Email Saved: " + this.email;
}
}
// A User
firstUser = new User("Richard", "[email protected]");
firstUser.changeEmail("[email protected]");
firstUser.saveScore(15);
firstUser.saveScore(10);
console.log(firstUser.showNameAndScores()); //Richard Scores: 15,10
console.log(firstUser.email); // [email protected]
// Another User
secondUser = new User("Peter", "[email protected]");
secondUser.saveScore(18);
console.log(secondUser.showNameAndScores()); //Peter Scores: 18
Let‘s expound on each line of code so we have a thorough understanding of this pattern.
The following lines initialize the instance properties. These properties will be defined on each User instance that is created. So
the values will be different for each user. The use of the this keyword inside the function specifies that these properties will be
unique to every instance of the User object:
this.name = theName;
this.email = theEmail;
this.quizScores = [];
this.currentScore = 0;
In the code below, we are overwriting the prototype property with an object literal, and we define all of our methods (that will be
inherited by all the User instances) in this object. Discussion continues after the code:
User.prototype = {
constructor: User,
saveScore:function (theScoreToAdd) {
this.quizScores.push(theScoreToAdd)
},
showNameAndScores:function () {
var scores = this.quizScores.length > 0 ? this.quizScores.join(",") : "No Scores Yet";
return this.name + " Scores: " + scores;
},
changeEmail:function (newEmail) {
this.email = newEmail;
return "New Email Saved: " + this.email;
}
}
This way of overwriting the constructor is simply for convenience, so we don‘t have to write User.prototype each time, like this:
User.prototype.constructor = User;
User.prototype.saveScore = function (theScoreToAdd) {
this.quizScores.push(theScoreToAdd)
};
User.prototype.showNameAndScores = function () {
var scores = this.quizScores.length > 0 ? this.quizScores.join(",") : "No Scores Yet";
return this.name + " Scores: " + scores;
};
JavaScript Prototype
In JavaScript, you add methods and properties on the prototype property when you want instances of an object to inherit those
methods and properties. This is the reason we add the methods on the User.prototype property, so that they can be used by all
instances of the User object.
Constructor Property - In my post JavaScript Prototype, I explained that every function has a constructor property, and this
property points to the constructor of the function. For example:
function Fruit () {}
var newFruit = new Fruit ();
console.log (newFruit.constructor) // Fruit ()
The one disadvantage of overwriting the prototype is that the constructor property no longer points to the prototype, so we
have to set it manually. Hence this line:
constructor: User
Prototype Methods - In the following lines, we create methods on the prototype (in the object literal) so that all instances of
Users can have access to these methods.
saveScore:function (theScoreToAdd) {
this.quizScores.push(theScoreToAdd)
},
showNameAndScores:function () {
var scores = this.quizScores.length > 0 ? this.quizScores.join(",") : "No Scores Yet";
return this.name + " Scores: " + scores;
},
changeEmail:function (newEmail) {
this.email = newEmail;
return "New Email Saved: " + this.email;
}
// A User
firstUser = new User("Richard", "[email protected]");
firstUser.changeEmail("[email protected]");
firstUser.saveScore(15);
firstUser.saveScore(10);
// Another User
secondUser = new User("Peter", "[email protected]");
secondUser.saveScore(18);
secondUser.showNameAndScores(); //Peter Scores: 18
As you see, we have encapsulated all the functionality for a User inside the User Function, so that each instance of User can make
use of the prototype methods (like changeEmail) and define their own instance properties (like name and email).
With this pattern, you can use the standard operators and methods on the instances, including the instanceOf operator, the for-
in loop (even hasOwnProperty), and the constructor property.
****************************************************************************************************************************************
In the example below, we define the class Student as a child class of Person. Then we redefine the sayHello() method and add
the sayGoodBye() method.
Person.prototype.sayHello = function(){
console.log("Hello, I'm " + this.firstName);
};
// Example usage:
var student1 = new Student("Janet", "Applied Physics");
student1.sayHello(); // "Hello, I'm Janet. I'm studying Applied Physics."
student1.walk(); // "I am walking!"
student1.sayGoodBye(); // "Goodbye!"
A First Example
Let‘s see OO and non-OO code together. We‘ll implement four operations:
Circle Rectangle
Area Area of Area of
Circle Rectangle
Perimeter Perimeter of Perimeter of
Circle Rectangle
In the non-OO style, the functions are emphasized. In the OO style, the objects (actually the objects‘ types) are emphasized:
Non Object-Oriented Style - Here the functions are emphasized. Functions operate on objects. Some people might say in this
case the things operated on are just data structures and not objects:
function area(shape){
if(shape.type === 'Circle'){
return Math.PI * Number(shape.radius) * Number(shape.radius);
} else if(shape.type === 'Rectangle'){
return shape.width * shape.height;
}
}
function perimeter(shape){
if(shape.type === 'Circle'){
return 2.0 * Math.PI * shape.radius;
} else if(shape.type === 'Rectangle'){
return 2.0 * (shape.width + shape.height);
}
}
const c1 = {type: 'Circle', radius: 5.0};
const c2 = {type: 'Circle', radius: 7.0};
const r1 = {type: 'Rectangle', height: 1.0, width: 2.5};
const r2 = {type: 'Rectangle', height: 10.0, width: 12.0};
console.log(area(c1)); // 78.53981633974483
console.log(area(c2)); // 153.93804002589985
console.log(perimeter(c1)); // 31.41592653589793
console.log(perimeter(c2)); // 43.982297150257104
console.log(area(r1)); // 2.5
console.log(area(r2)); // 120
console.log(perimeter(r1)); // 7
console.log(perimeter(r2)); // 44
Object-Oriented Style
In the OO style, objects (unlike dumb ―data structures‖) have behavior. We often see OO code written with the behavior
specification for an object (or object family) rolled into a class or interface or prototype or protocol. Here‘s a JavaScript
specification using the class syntax:
class Circle{
constructor(r){
this.radius = r;
}
area(){ return Math.PI * this.radius * this.radius; }
perimeter(){ return 2.0 * Math.PI * this.radius; }
}
class Rectangle{
constructor(w, h){
this.width = w;
this.height = h;
}
area(){ return this.width * this.height; }
perimeter(){ return 2.0 * (this.width + this.height); }
}
In the expression c.area(), c is called the receiver and area is called a method.
Previously we saw that JavaScript‘s class syntax actually was nothing more than a nice way to define a constructor function and a
protype object to be used for all objects created with that constructor. Don‘t confuse this keyword with ―real‖ classes that you
might see in a language like Smalltalk or Ruby or even Java.