Object-oriented programming is a programming paradigm that uses objects to represent real-world things. An object is a data structure that contains data and methods that can be used to manipulate that data. In OOP, objects are organized into classes, which define the properties and methods that objects of that class will have. JavaScript is a little different from other classic programming languages in that it is fully object-oriented, but it uses prototype-based programming rather than class-based programming.
To create objects, we can use constructor functions. A constructor function is a special type of function that is used to create objects. Here's an example:
function Ship() {
this.floats = true;
this.material = 'steel';
}
const myShip = new Ship();
console.log(myShip.floats); // Output: true
console.log(myShip.material); // Output: steel
Ship
that has two properties, floats
and material
. We then instantiated a new object of the Ship
class called myShip
. We can access the properties of myShip
using dot notation.floats
property of the myShip
object like this:console.log(floats); // Output: ReferenceError: floats is not defined
To access the properties of an object, we must use dot notation or bracket notation:
console.log(myShip.floats); // Output: true
console.log(myShip['material']); // Output: steel
Inheritance works a little differently than in other classic programming languages. JavaScript uses prototype-based programming, which means that each object has a private property called the prototype, which can have a prototype of its own. When we access a property or method on an object, JavaScript walks up the prototype chain to find it.
Here's an example:
const myObject = {
a: 1
};
const myOtherObject = Object.create(myObject);
myOtherObject.b = 2;
console.log(myOtherObject.a); // Output: 1
console.log(myOtherObject.b); // Output: 2
myObject
with a property a
. We then created another object called myOtherObject
using Object.create()
, which takes myObject
as its argument. We added a property b
to myOtherObject
. When we access the property a
on myOtherObject
, JavaScript walks up the prototype chain to find it on myObject
.The prototype chain is a fundamental concept in JavaScript that provides a mechanism for inheritance.
Each object has a private property called theprototype
, which can have a prototype
of its own. Essentially, the prototype chain works by walking up the chain until it finds the method or property it's looking for, and it checks if it exists in the instance itself, then in the prototype of the instance, and so on until it reaches the end of the chain, which is the object prototype. This is how JavaScript does inheritance differently than some of the other traditional class-based object-oriented languages, where you define a class and then create an instance of that class.
Here's an example to illustrate the concept of the prototype chain:
const myObject = { a: 1 };
console.log(myObject.a); // 1
console.log(myObject.toString()); // [object Object]
myObject
, which has a property called a
. We can access this property using dot notation. The second console.log statement demonstrates how the prototype chain works. The toString()
method is called on myObject
, but this method is not defined on the instance of the object. JavaScript then looks up the prototype chain to see if the method is defined on the prototype of the object. In this case, it is defined on the Object.prototype
, so it returns the default toString()
method.hasOwnProperty()
hasOwnProperty
is a method of the Object
constructor that returns a boolean value indicating whether the object has the specified property as its own property (as opposed to inheriting it from the prototype chain).Here's an example to illustrate the hasOwnProperty()
method:const myObject = { a: 1 };
console.log(myObject.hasOwnProperty('a')); // true
console.log(myObject.hasOwnProperty('toString')); // false
hasOwnProperty()
method on myObject
to check if it has the properties a
and toString
. The method returns true
for a
because it is an own property of the object. However, it returns false
for toString
because it is not an own property of the object, but rather it is inherited from the prototype chain.To create an object constructor, we use a function. Here's an example:
function Animal(name, age, breed) {
const obj = {};
obj.name = name;
obj.age = age;
obj.breed = breed;
return obj;
}
name
, age
, and breed
. We then create an object obj
and set its properties to the values passed in as arguments. Finally, we return the object.new
keyword in conjunction with our constructor function. Here's an example:const dogOne = new Animal('Spike', 3, 'Labrador');
dogOne
with properties name
, age
, and breed
set to 'Spike', 3, and 'Labrador', respectively.prototype
property. Here's an example:Animal.prototype.sayBreed = function() {
console.log(`My breed is ${this.breed}`);
};
sayBreed
to the Animal
prototype, which we can then use on any object created by the Animal
constructor.When creating Constructor Functions, it is often necessary to create more specialized objects that inherit from a more generic parent object. This is where Constructor Functions that invoke another Constructor Function come in. This pattern allows us to create a more specialized Constructor Function that inherits from a more generic parent Constructor Function.
To create a Constructor Function that invokes another Constructor Function, we use thecall()
method. The call()
method allows us to call a function that is defined elsewhere in the current context. In the context of a Constructor Function, this
refers to the new object being created. We can pass this
as the first argument to the call()
method, which will allow the new object to inherit properties and methods from the parent object.Here's an example of creating a more specialized Constructor Function that invokes another Constructor Function:
function Animal(name, age) {
this.name = name;
this.age = age;
}
Animal.prototype.makeNoise = function() {
console.log('Generic animal noise');
}
function Dog(name, age, breed) {
Animal.call(this, name, age);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('Bark bark woof');
}
const myDog = new Dog('Rufus', 3, 'Labrador');
myDog.makeNoise(); // Generic animal noise
myDog.bark(); // Bark bark woof
Animal
, which takes in name
and age
as arguments and creates a new object with those properties. It also has a makeNoise
method, which is inherited by any child Constructor Functions.We then have a more specialized Constructor Function Dog
, which takes in name
, age
, and breed
as arguments. We use the call()
method to call the Animal
Constructor Function, passing this
as the first argument to inherit the name
and age
properties. We then set the breed
property on the new object.We then use Object.create()
to create a new object that inherits from Animal.prototype
, which allows the new Dog
object to inherit the makeNoise
method. We set the constructor
property of the Dog.prototype
object to Dog
so that it points to the Dog
Constructor Function.Finally, we add a new bark
method to the Dog.prototype
object, which is a specialized method for Dog
objects.When we create a new Dog
object using the new
keyword, we can see that it inherits the properties and methods from the parent Animal
object, as well as the specialized bark
method.function
keyword, we use the class
keyword to declare a class. class Person {
// class methods
}
constructor
method is a special method used for creating and initializing an object created with a class. It has to be called constructor
and it can accept parameters. class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
We can declare a class using two ways:
We can use the class declaration to declare a class. It is a class statement that defines a new class with a given name using "class" keyword.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
We can also use a class expression to define a class. It is similar to function expression syntax.
const Person = class {
constructor(name, age) {
this.name = name;
this.age = age;
}
};
greetings
method to a Person
class:class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greetings() {
console.log("Classes are cool.");
}
}
extends
keyword. The super
keyword is used to call the parent's constructor and to call the parent's methods.
class Employee extends Person {
constructor(name, age, position) {
super(name, age);
this.position = position;
}
}
Employee
class extends the Person
class and it has a position
property. The super
keyword is used to call the Person
class's constructor, passing the name
and age
parameters to it.
Static methods are methods that belong to a class itself, rather than an instance of the class. This means that they are called on the class itself, rather than on an object created from the class.
static
keyword. We then define the method as we would any other method.class Customer {
static sayHi() {
console.log('Hi with a smiley face');
}
}
To call a static method, we use the class name, followed by the method name.
Customer.sayHi();
Static methods are inherited just like any other property of a class. For example:
class Person {
static sayHey() {
console.log('Hey');
}
}
class Customer extends Person {}
Customer.sayHey(); // Output: 'Hey'
Static methods are useful when we want to define a method that belongs to a class itself, rather than to an instance of the class. For example, we might use a static method when we want to perform some kind of comparison or logic on multiple instances of a class. We might also use a static method when we want to group together related functionality in a class.