How to REALLY Learn JavaScript Series: Part 6 Object-Oriented Programming

Object-oriented programming is an advanced way to make use of JavaScript data types as a whole. This type of programming works for most languages available, but our focus, of course, is JavaScript in this series. Objects can be custom tailored to contain virtually any groups of data you may need. This data can then be accessed using properties or methods you set in place.

A great way to develop using JavaScript is to make use of objects. This post will help you understand the advantages of objects and how to go about using them in your own projects.

What is an Object?

I discussed briefly what an object is in Part 2 of this series. Essentially an Object is a way to group data together that you can later access whenever you need to.

The simplest way to create an Object in JavaScript is to create a new instance of an Object and add properties or methods to it. Here’s an example:

// Set new instance to variable
var car = new Object();
// Assigning properties to car Object
car.color = 'black';
car.model = 'Lexus';
car.topSpeed = 140;

car.sayModel = function() {
   alert(car.model);
};

A more modern approach to writing the same Object above is to use object literal notation like this:

var car = {
// Assigning properties to car Object
color : 'black',
model : 'Lexus',
topSpeed : 140,

sayModel : function() {
   alert(this.model);
    }
};

I personally prefer the object literal notation way of writing an Object for the simple fact that I’m no longer repeating the Object name within each property inside the Object itself.

Types of Properties

There are two types of properties within Objects.

  • Data Properties
  • Accessor Properties

Data Properties

Data Properties contain a single location for a data value. These data values can be read and written to this location. These properties have four types of attributes which can describe their behavior.

  1. Configurable — This indicates whether the property can be redefined by removing the property and changing the properties attributes.
  2. Enumerable — This indicates if the property will return in a for-in loop. This is true by default.
  3. Writable – This indicates if the property’s value can change. This too is true by default.
  4. Value – Contains the actual data. The default value is undefined.

When every property is set to true you will notice a familiar looking Object form is written like this:

var house = {
    squareFeet: 2200;
};

Here I created an Object called house. I then created a property called squareFeet and assigned it the value of 2200. This means the Value is set to 2200. Any changes to this value are stored in the same location.

Creating a configurable property within an object is a little more advanced. Doing so requires the use of the Object.defineProperty() method introduced in ECMAScript 5. Check out the example:

var house = {};
Object.defineProperty(house, "squareFeet", {
    configurable: false,
    value: 2200
});

alert(house.squareFeet); // 2200
delete house.squareFeet;
alert(house.squareFeet); // 2200

Setting configurable to false means the property cannot be removed from the object. I tried to delete the property, but nothing happened thanks to our Object.defineProperty() method and the configurable option.

Along with configurable you can use the Object.defineProperty() method in the same way for the attributes Value, Enumerable, and Writeable as defined above.

In most cases, you probably won’t make use of this method. Knowing the ability to configure different attributes is quite nice though. It also makes understanding Objects a little less painful if you can wrap your head around it.

Accessor Properties

Accessor Properties do not contain a data value. Instead, they contain getters and setters. What is a getter and setter you might ask?

  • Getters – simply put, a getter is a method that gets a value of a specific property.
  • Setters – a setter is a method that sets the value of a specific property.

An easier way to describe what getters and setters do is to see them in action[1]:

var book = {
  _year: 2004, 
  edition: 1
};

Object.defineProperty(book, "year", {
  get: function() {
    return this.book;
  },
  set: function(newValue) {
    if (newValue > 2004) {
      this._year = newValue;
      this.edition += newValue - 2004;
    }
  }
});

book.year = 2005;
alert(book.edition); // 2

This code defines two default properties within a book Object: first year and edition. I make use of the Object.defineProperty() method to create getter and setter functions. The getter function returns the books current values. The setter function allows me to pass new parameters thus updating the code output base on the entry.

I altered the book year to 2005. The result is the edition increasing to the second.

Important Notes:

  • You may notice the property _year having an underscore at the beginning. The reason for this is the result of a common notation to indicate that this property is not intended to be accessed outside of the object’s methods.

  • It’s not actually possible to define an accessor property explicitly; you have to use the .Object.defineProperty() as explained before. This works only in IE9 and up.

  • It’s not always necessary to assign both a getter and a setter function. Assigning just a getter means the property cannot be written to and is just ignored. A property with only a setter cannot be read and will return undefined. Both functions will throw errors in this case if in strict mode.

The Art of Creating Objects

A common trend these days is to utilize the DRY(Don’t Repeat Yourself) approach when writing code. Duplicating code results in poorer performance, a messy code base, and more work for yourself in the end if you need to update the code at some point. Luckily there are alternate patterns set in place when it comes to writing Objects.

The Factory Pattern

The factory pattern is a well-known pattern use in a lot of different types of software engineering. JavaScript doesn’t have the ability to define classes like other programming languages. This very notion is why we turn to object-oriented programming to achieve the same result as a class could within our code.

A basic example of the Factory Pattern:

function createUser (name, email, username) {
  var o = new Object();
  o.name = name,
  o.email = email,
  o.username = username,
  o.userGreeting = function() {
    alert('Welcome ' + this.name);
  };
  return o;
}

var user1 = createUser("Andy", "webcrunchblog@gmail", "webcrunch");
document.write(user1.name); // Andy

In the factory pattern, I create a function to accept parameters ( three in this case) to be later accessed when creating new users within the createUser Object. This function has no maximum to the amount of times it can be called. This solved our problem of creating multiple similar objects, but it did not solve the object identification issue, or what type of object it is. Enter the Constructor Pattern…

The Constructor Pattern

Constructors are used to create specific types of objects. Native constructors we know all too well are the Object and Array constructors. You can define custom constructors that define properties and methods all on their own. Below I’ll elaborate by using the code from the previous example:

function User(name, email, username) {
  this.name = name;
  this.email = email;
  this.username = username;
  this.userGreeting = function() {
    alert('Welcome ' + this.name);
  };
}

var user1 = new User("Andy", "webcrunchblog@gmail", "webcrunch");
document.write(user1.name);

In this example I replace the createUser() function within the Factory example with the User() function. The code inside the function is all the same minus how we assigned the properties. I made use of the this attribute as opposed to initializing a new object. Benefits of this constructor pattern are:

  • Not having to explicitly create an object. Less code to write.
  • I make us of the this object which is referenced by the User function
  • I didn’t have to write a return statement. Heck yes.

One thing to note is that the name of the new function is User with a capital “U”. By common convention contractor functions typically begin with a capital letter to help developers identify their purpose right away. This convention stems from other object-oriented languages. Constructor functions essentially are functions used to create Objects.

Creating a new instance of a User in my example naturally makes use of the new operator. Calling a constructor in this way results in four steps.

  1. Create a new object
  2. Assign the this value of the constructor to the object you just created
  3. Execute any code inside the constructor
  4. Return the new Object

The Prototype Pattern

Any function that is created comes with a Prototype pattern associated with it. This Prototype property is typically within an Object containing properties and methods available to instances of a specific reference type. That was probably confusing as hell right? In simplest terms, a prototype is an object from which other objects inherit properties. An object can be a prototype. All objects have prototypes by default.

How Prototypes Work
When a function is created, it’s prototype property is created alongside. By default, all prototypes get a property called constructor that points back to the function itself. Take the example above, the User.prototype.constructor points back to the top level User() function. Any properties within the object itself get inherited within the prototype.

A real worldly example:

var Circle = function(radius) {
    this.radius = radius;
}
 
Circle.prototype.area = function() {
   return Math.PI*this.radius*this.radius;
}
 
var a = new Circle(3); 
var b = new Circle(4);

document.write(a.area().toFixed(2)); //28.27
document.write(b.area().toFixed(2)); //50.27

And another found on stack overflow here which makes use of Google’s map API by targeting its overlay properties via the prototype pattern.

Prototype constructors can be confusing as all hell. I personally make most use of the constructor pattern or factory pattern, but there are times you don’t always have the luxury of author code the way you want. With that in mind, the Prototype pattern is a safe bet.

Namespacing

A namespace is a container that allow developers to group functionality under a unique name. Doing this is essentially the same as creating an object in JavaScript that you can later get access to in other functions or methods you may be working on.

Creating a global namespace can be done with the following syntax:

// global
var APP = APP || {};

This code essentially checks if APP is already defined or not and if not it creates a global object.

APP.myMethods = {
  name: "",
  phone: "",
  validateName: function(name) {
    // Do something with the name  
},
  validatePhone: function(phoneNumber) {
   // Do something with phone number
  }
}

APP.myEvents {
  addListener: function(e, type, fn) {
   // stuff to do when an event is triggered
  },
  removeListener: function(e, type, fn) {
   // stuff to remove when an event is triggered 
    }
 }

// Making use of the myEvents method:
APP.myEvents.addListener("div", "type", someCallback());

The code above is namespacing at work. Here I use our APP namespace or Object and create functionality that our entire application has access to based on what methods I call using dot notation and chaining.

OOP Terminology

There are a lot of terms thrown around in a given programming language. Below I’ll help clear some of the cobwebs you may have with some of the terms I’ve used in this series so far. Please note that all of these terms and their definitions come from Mozilla’s developer documentation

Namespace
A container which lets developers bundle all functionality under a unique, application-specific name.

Class
Defines the object’s characteristics. A class is a template definition of an object’s properties and methods.

Object
An instance of a class.

Property
An object characteristic, such as color.

Method
An object capability, such as walk. It is a subroutine or function associated with a class.

Constructor
A method called at the moment an object is instantiated. It usually has the same name as the class containing it.

Inheritance
A class can inherit characteristics from another class.

Encapsulation
A method of bundling the data and methods that use the data.

Abstraction
The conjunction of an object’s complex inheritance, methods, and properties must adequately reflect a reality model.

Polymorphism
Poly means “many” and morphism means “forms”. Different classes might define the same method or property.

Finish

Hopefully this post has helped you start to wrap your head around Objects and how JavaScript makes use of them as the result of classes not being available within the language. Objects help us use code across entire applications in the most efficient way that is less taxing and easier to understand for both legacy and modern day browsers.

[1]Professional JavaScript for Web Developers

The Series So Far