I understand the main principle of OOP and I somewhat know how to implement it into JS.
function Person(name) {
this.name = name;
this.speak = function(msg) {
console.log('Person says:' + msg);
}
}
var dad = new Person('David');
dad.speak('I am your dad!');
The script above does nothing more than printing out a message in the console. I don't understand how we approach the DOM with this technique. Maybe something like this?:
function Person(target, name) {
this.target = target;
this.name = name;
this.speak = function(msg) {
this.target.find('.speech-bubble').html(msg);
}
}
var dad = new Person($('#dad'), 'David');
dad.speak('I am your dad!');
Although this doesn't seem like a good approach.
How do we manipulate the DOM with objects, methods, constructors etc. through OO Javascript?
In relation to OO, if you are going to adopt for your DOM facing code, you are not too far of.
I'd say that a class should represent a component/element on the DOM. With it's methods being the state management part. But there is no correct answer here to be honest. This is but one way of designing OO with the DOM facing part.
Example:
const basicClassName = 'component';
const basicTemplate = '<h1>This is my basic component</h1>';
class MyComponent {
constructor(template = basicTemplate, className = basicClassName) {
this.template = template;
this.className = className;
this.element = document.createElement('div');
this.element.className = className;
this.element.innerHTML = template;
this.element.onclick = this.onClick.bind(this);
this.element.style.cursor = 'pointer';
}
onClick() {
this.element.classList.toggle('clicked');
}
}
const component = new MyComponent();
const container = document.querySelector('.container');
container.appendChild(component.element);
body {
font-size: 14px;
}
.component {
display: block;
padding: 1.3em;
box-shadow: 1px 1px 4px lightgray;
}
.clicked {
background-color: papayawhip;
}
<div class="container"></div>
What you need to understand is the concept of the Prototype
.
When you create an instance using new
, you are constructing an object based upon a prototype.
Consider the following:
function Person(name) {
this.name = name;
this.speak = function (msg) {
console.log('Person says:' + msg);
};
}
var dad = new Person('David');
dad.speak('I am your dad!');
console.log('Is dad.speak equal to dad.speak?', dad.speak === dad.speak);
var mom = new Person('David');
console.log('Is mom.speak equal to dad.speak?', mom.speak === dad.speak);
Each time you construct a new instance of Person
, a new speak
prototype now floats around somewhere in your logic. This is very inefficient.
To fix this, we need to modify the prototype
of our function:
function Person(name) {
this.name = name;
}
Person.prototype.speak = function (msg) {
console.log('Person says:' + msg);
};
var dad = new Person('David');
dad.speak('I am your dad!');
console.log('Is dad.speak equal to dad.speak?', dad.speak === dad.speak);
var mom = new Person('David');
console.log('Is mom.speak equal to dad.speak?', dad.speak === dad.speak);
This way, we only have the function created once, on the prototype
which is inherited to all instances. This is easier to maintain and a lot more efficient.
Now we can extend DOM object via their prototype
, but it isn't recommended because you start to mess with the web standards, making troubleshooting much more difficult.
Array.prototype.isLengthGreaterThanFive = function(thisArg) {
return this.length > 5;
};
console.log([1, 2, 3, 4].isLengthGreaterThanFive(), [1, 2, 3, 4, 5, 6].isLengthGreaterThanFive());
A better way of handling this is to create extended objects or to simply use functions:
//Using functions
function isLengthGreaterThanFive(array) {
return array.length > 5;
}
console.log(isLengthGreaterThanFive([1, 2, 3, 4]), isLengthGreaterThanFive([1, 2, 3, 4, 5, 6]));
//Using a wrapper class
var MyArray = (function() {
function MyArray(array) {
if (array === void 0) {
array = [];
}
this.array = array;
}
MyArray.prototype.isLengthGreaterThanFive = function() {
return this.array.length > 5;
};
return MyArray;
}());
console.log(new MyArray([1, 2, 3, 4]).isLengthGreaterThanFive(), new MyArray([1, 2, 3, 4, 5, 6]).isLengthGreaterThanFive());
The benefits of using a class is that we can extend upon our idea of the object:
//Base class
function Person(firstname, lastname, says) {
if (firstname === void 0) {
firstname = "Leonado";
}
this.firstname = firstname;
if (lastname === void 0) {
lastname = "Da Vinci";
}
this.lastname = lastname;
if (says === void 0) {
says = "hello";
}
this.says = says;
}
//Base methods
Person.prototype.iAm = function () {
return this.firstname + " " + this.lastname;
};
Person.prototype.Speak = function () {
return this.says + " my name is " + this.iAm();
};
//Extended class
function Warrior(firstname, lastname, says) {
//Call in constructor
Person.call(this, firstname, lastname, says);
}
//Inheriting
Warrior.prototype = Object.create(Person.prototype);
Warrior.prototype.constructor = Warrior;
//Overruling "Speak"
Warrior.prototype.Speak = function () {
return "My name is " + this.iAm() + ", " + this.says;
};
console.log([new Warrior("Peter", "Allan", "Ahoyhoy").Speak(), new Person("Peter", "Allan", "Ahoyhoy").Speak()]);
In the example above we extend the prototype of Person
for Warrior
so that we retain the functionality of Person
, and then simply modify what's different about a Warrior
. This way we get to reuse the prototype method iAm
, and we can focus on only changing what needs to change in the Speak
method.
EDIT 1
I noticed too late that the question had changed a little.
You can treat DOM elements like any other class in JavaScript. The following setup has all Persons
sharing a single DIV
to speakUp
:
var Person = (function () {
function Person(age, firstname, lastname) {
if (age === void 0) { age = 50; }
if (firstname === void 0) { firstname = "Peter"; }
if (lastname === void 0) { lastname = "Venkman"; }
this.age = age;
this.firstname = firstname;
this.lastname = lastname;
}
Person.prototype.speakUp = function () {
Person.bubble.innerHTML = this.firstname + " " + this.lastname + " is " + this.age + " years old";
};
return Person;
}());
Person.bubble = document.createElement("div");
document.body.appendChild(Person.bubble);
setInterval(function () {
var p = new Person(Math.floor(Math.random() * 100));
p.speakUp();
}, 3000);
This could easily become a DIV
per Person
, or a refereced DOM object (document.getElementById) shared among all Person
s.
EDIT 2
In response to your comment:
In JavaScript everything is in essence and object
. You create a function it registers an object
with the functions name and returns and instance
of that object
. Everything like Arrays
, Strings
, DOM
elements and custom functions has some master object
hidden behind the scenes. Every time a new Array
or DOM
element or whatever is created, it has a reference to its master object (called the prototype). This is called the prototype chain.
If you look on my second example above, when dad.speak
is called JavaScript first searches the instance for a speak
property, but it won't find one because we haven't assigned it the way we did in example one were it was instance specific.
JavaScript will then try one level up the prototype
chain and here it will find a matching property and use this instead. This way we can alter the default behavior of custom OR existing elements in JavaScript.
The idea being, that if you have some property that all instances of a prototype should have, then we simply modify the prototype once and they will all inherit
this property.
Think of it this way. If you were to describe all living things on earth in JavaScript you would want some form of groupings. For instance the first level would be something like an Exists
object that carries a property for a name and an id. From here you you could create Plant
and Animal
and have them both inherit the prototype of Exists
. Now we could create a Flower
class that inherits Plant
and a Rose
class that inherits Flower
and so on.
The idea is to apply you properties in a way that makes sense to human beings via inheritance (an owl can fly because it is a bird / a shark can swim because it is a fish). Binding them at the level that makes sense, inheriting in a logical pattern and using your time efficiently.
If you are still confused, try looking up prototype
tutorials.
Here is a good Youtube video to explain it:
https://www.youtube.com/watch?v=PMfcsYzj-9M
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With