Classical Object-Oriented Programming in JavaScript

April 7, 2009 by Andrew Noyes

IMG_1185Prototypal inheritance is a confusing subject for programmers who are used to the Classical approach to creating objects in other programming languages, like Java and C++. In addition, most other programming languages used on the web, like PHP and Python, also use Classical OOP.

Prototyping is elegantly simple, and is part of what makes OOP in JavaScript so expressive. You can do a lot more with fewer lines of code in JavaScript than you can in other languages, owing entirely to the fact that all objects in JavaScript inherit their properties and methods from other objects. At the top of this chain is the Object object.

I could spend all day discussing which object-oriented paradigm is superior and we would never reach a conclusion, since each has its strengths and weaknesses.

While I highly recommend that if you're coming from another language you take the time to familiarize yourself with Prototypal Inheritance, it is not my goal here to convince you to use it. I personally think it's best to use Prototypal Inheritance, but I will not deny that there are many scenarios where a classical approach is beneficial, particularly when working in a team. The Class, the heart of classical OOP, can be functionally simulated in JavaScript.

The Constructor

Creating a class in classical OOP is an iterative process whereby you continually modify your class throughout the development of your program or script. However, there is one key component that every class needs: a constructor.

In JavaScript, the constructor is a blueprint for building an object. It tells the JavaScript interpreter what values (variables), which are called object members in OOP, that the new object will contain, and sets default values. Without further ado, here is an example of a constructor:

  1. function MyClass(num) {
  2. this.num = num;
  3. }

Of course, this can be seen anywhere on the internet. What I'm going to introduce here is the concept of access modification, whereby we designate that certain object members can only be touched by object methods, as opposed to allowing the script to interface with object members directly, using the syntax myObject.myValue.

Object members and methods that can be called directly are said to be public. On the flip side, objects and members that can only be referenced from within the object itself are said to be (you guessed it) private.

Languages that implement a well-defined classical approach to OOP will have the keywords public and private. These keywords are called access modifiers. For example, in PHP5, to declare a public member called $myNum, you would do something like this:

  1. <?php
  2. class MyClass {
  3. // Public member
  4. public $myNum;
  5.  
  6. // Class constructor
  7. public function __construct($num) {
  8. $this->myNum = $num;
  9. }
  10. }
  11. ?>

JavaScript posseses no such access modifiers. They are instead implicit; we can simulate the functionality of access modifiers by exploiting JavaScript's unique scoping.

Function Scope

If you're used to programming in languages like C, you're probably inclined to block scope, where any variables that are declared within a block are destroyed once the block scope is complete. Consider the following C program:

  1. #include <stdio.h>
  2.  
  3. int main() {
  4. int myInt = 1;
  5.  
  6. for (int i = 0; i < 10; i++) {
  7. int temp = i + myInt;
  8. printf("The value of temp is %d.", temp);
  9. }
  10.  
  11. // Compiler error because temp isn't declared in this scope
  12. printf("The value of temp is %d.", temp);
  13.  
  14. return 0;
  15. }

The variable temp is declared within the for-loop block. Once that loop finishes execution, the block scope is terminated and the variable is deleted. It cannot be accessed from the next level up, the main() function.

On the flip side, let's take a look at how JavaScript handles this:

  1. function sillyFunction() {
  2. var myInt = 1;
  3.  
  4. for (var i = 0; i < 10; i++) {
  5. var temp = myInt + i;
  6. // Outputs 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  7. alert("The value of temp is " + temp);
  8. }
  9.  
  10. // Outputs 10
  11. alert("The value of temp is " + temp);
  12. }
  13. sillyFunction();

As you can see, in JavaScript, it doesn't matter where in the function you declare a variable, be it nested in a block or in the root function block, so long as you've declared the variable before you attempt to use it. For this reason, the following code is functionally equivalent to the last:

  1. function sillyFunction2() {
  2. var myInt = 1, i, temp;
  3.  
  4. for (i = 0; i < 10; i++) {
  5. temp = myInt + i;
  6. // Outputs 1, 2, 3, 4, 5, 6, 8, 9, 10
  7. alert("The value of temp is " + temp);
  8. }
  9.  
  10. // Outputs 10
  11. alert("The value of temp is " + temp);
  12. }
  13. sillyFunction2();

There are actually very good reasons for declaring all of your variables in one line, namely for debugging purposes. If you declare all of your variables in one spot, there's no hunting through your program to find what value you initialized that variable at.

Private, Public, and Privileged Methods

Using this unique scope, we can create methods that are both accessible to everyone, and have access to private variables. Here's a constructor with a private variable and a public method:

  1. function MyConstructor() {
  2. // Private variable
  3. var num = 1;
  4. }
  5.  
  6. MyConstructor.prototype.getNum = function () {
  7. return this.num; // Gah! this.num doesn't exist!
  8. };

"But wait", you're thinking, "we didn't assign num as a member using the this keyword. It doesn't exist." It is true that in its present form, getNum won't work, however it is a misconception to think that num doesn't exist.num will continue to exist until the object is destroyed, it just isn't accessible from the outside. However, think about the function scope that I just discussed. Because JavaScript uses function scope, any blocks contained within the MyConstructor function has access to variables declared in MyConstructor. If we re-engineer our getNum method like this...

  1. function MyConstructor() {
  2. var num = 1;
  3. this.getNum = function () {
  4. return num;
  5. };
  6. }

Because of the function scope, getNum has access to the private num variable, which is local to MyConstructor. getNum is said to be a privileged function.

There is only one more piece to the OOP puzzle:

  1. function MyConstructor() {
  2. // Private variable
  3. var num = 1;
  4.  
  5. // Private method
  6. function addOne() {
  7. return num + 1;
  8. }
  9.  
  10. // Privileged method
  11. this.getNumPlusOne = function () {
  12. return addOne();
  13. };
  14. }

When to use private, privileged, and public variables is at your discretion. I'm leaving out a lot here, so it's up to you to look into a general approach to classical OOP. In doing so you will learn how to create classes, and how to choose whether a method should be public or private.

← Home