3

I can't get my head around OOP in JavaScript.

Many years ago, I used to dabble in programming BASIC and learned a bit of COBOL and FORTRAN in school so I am somewhat familiar with procedural programming, but I never learned any of these to a very high level of proficiency or complexity. I've also done a few hackey things with JS but I guess it was mostly procedural in nature rather than OOP.

Recently, I've decided to train myself to become a web developer and learn JavaScript properly, and based on the rave reviews, I decided on Head First JavaScript Programming as my textbook.

The problem I am having is that I can't figure out how or why using objects is really any different or better than using functions and variables. I've looked around at a ton of tutorials and videos but they all say the exact same thing as the book. "Objects are useful because they are analogous to everyday items in real life such as a car. A car has a model year, a make, a weight, a color, etc... A car can also do things such as start, stop, go, etc. They are functions inside the object and called 'methods'." and that is followed by "this is how to create an object:"

var myCar = {
    make: "Chevy";
    year: 2004;
    kilograms: 2000;
    color: "blue";
    go: function() {
        alert("go!");
    }
};

Great. Now I know that objects are somehow analogous to real life things and I know how to make one with properties and methods. I get that.

And I can access these property values by calling them as follows:

a = myCar.make;
b = myCar.year;
c = myCar.color;

I can also call the function (or method):

myCar.go();

Wonderful. But I STILL can't figure out WHY I want to do it this way. Why is this better than the following?

myCarMake = "Chevy";
myCarYear = 2004;
myCarKgs = 2000;
myCarColor = "blue";

function go() {
    alert("go!");
};

Other than how the code is organized, I don't understand how it's any less procedural. Statements are executed in order, after all. And I don't see what the advantages are to doing all of this.

I keep thinking that what I really need in order to understand this is to see two programs that do the same thing, one coded procedurally with normal variables and functions, and the second one programmed with OO in order to see the difference and how these objects interact with each other in an advantageous way.

I find that all the textbooks and websites that I have found never explain how similar things behave differently or why one is better than the other and there are few if any examples of how to tie it all together well. Most books and tutorials just tell you what you can do but not why you'd want to do that or choose one way over an other. (Irrelevant to this question but another thing I'm wonder about is, I know I can assign a function to a variable but WHY would I want to do that?)

To be clear about what I am looking for, my question is, can someone show me a program that is programmed both ways (both do the same thing and are simple enough for a beginner but complex enough to show why OOP might be needed or advantageous) to highlight the differences and explain why it's better?

AlwaysLearning
  • 311
  • 4
  • 12
  • To call it OOP you need polymorphism, encapsulation and inheritance. – PVL Apr 23 '16 at 20:09
  • "I know I can assign a function to a variable but WHY would I want to do that": passing functions around is part of "functional programming" paradigm. It is useful when there is no "state" to pass around so that instead of passing an object with functions you just pass functions. This usually makes things more concise and clean. Functional style in Javascript deserves special attention and you'll learn a lot from books/tutorials on that. – Wiktor Zychla Apr 23 '16 at 20:41
  • @PVL Actually, nobody agrees on what OOP is. All traits commonly attributed to OOP can be found in other paradigms, often with a 'stronger' implementations of them. I think the only unique aspect of OOP are constructors. – jmrah Apr 23 '16 at 23:39
  • @Pang I see. OK, thank you for that. I'll re-edit my question. – AlwaysLearning Apr 24 '16 at 06:53

3 Answers3

2

Truthfully, I always found the idea of properly scoped variables a good argument for the OOP approach of javascript.

Taking your example for instance:

var myCar = {
make: "Chevy",
year: 2004,

};

var make = "Ford";
console.log(make);
console.log(myCar.make);

Yields "Ford", then "Chevy". The reason scoping can be an issue within JS is the sheer number of libraries that can be pulled in. Yes small programs tend to be easier to write using procedural. But when you are pulling in a dozen other libraries, you do not know which approach they use for declaring variables - which can lead to obscure errors.

Yes, you could just declare the variables within the function definition and they would then be scoped to just that function (example below), but then they can't easily be reused!

function go(){
var make = "chevy";
console.log(make);
};
go();
console.log(make) ///throws undefined 'error'

OOP provides an approach that is scope safe, and easy to reuse (which is great for external libraries).

  • Thanks for the answer Eric. Maybe I don't understand scoping properly, but how is the object better? Aren't myCar.make and make both global? That does give me a little more than I had before! However, is there somewhere that I could see concretely how the paradigms differ side-by-side with respect to scoping (or other advantage)? I think this might be where I'm falling flat? Maybe I don't know enough to understand why OOP is better. But I would expect a textbook to teach things in way that would address that. – AlwaysLearning Apr 23 '16 at 20:10
  • @AlwaysLearning: with clean scoping comes easier, automatic memory management. If each object declares its own state and the state consumes memory, an out-of-scope object is disposed together with the memory it occupies. – Wiktor Zychla Apr 23 '16 at 20:17
2

In practice, if you have a small script or application, you will not see the difference. But once you move towards larger applications and bigger code bases, you'll see that OOP works much better than PP.

Here are some high-level advantages of the OOP approach over the PP:

1) Modularity and maintainability

The code becomes modular, the related data and methods are packaged into objects.

This makes it much easier to keep the code under control. It is complex to see this on few lines of code, but as your code grows, the procedural code most often turns into a mess.

With OOP your code is naturally splitted into smaller parts (objects) and even as the codebase grows, it is still easy to keep the code in order. The modularity leads to better maintainability - it is easier to modify and maintain the code.

2) Flexibility

It is much easier to replace the part of the code with some different functionality.

For example, you can have the Logger object which writes logs to files. But then you decide to write logs to the database instead.

With PP approach you would need to go over the whole code base searching for something like log_to_file(message, file_name) and replace with something like log_to_db(message, db, table_name).

With OOP you just create the new DbLogger and "plug it" into the system instead of the previous file logger, so the code which logs data will still look the same, like logger->log(message). Even better, you can decide on the type of the logger run-time, for example, read the setting from the configuration file and create whether file logger or db logger.

3) Testability

With OOP it is much easier to take our part of the code (object) and test it alone. If it depends on other objects, these can be replaced with fake implementations (test mocks), so you can really test the piece of code alone.

Just to demonstrate the difference, let's imagine that instead of one car, you now have three (also the go method should really do something with the variables, otherwise it doesn't make much sense):

var myCarMake = "Chevy";
var myCarYear = 2004;
var myCarKgs = 2000;
var myCarColor = "blue";

var yourCarMake = "Ford";
var yourCarYear = 2001;
var yourCarKgs = 1990;
var yourCarColor = "white";

var hisCarMake = "Ferrari";
var hisCarYear = 2011;
var hisCarKgs = 2990;
var hisCarColor = "red";

function go(make, year, kgs, color) {
    alert(make + " " + kgs + " " + year + " " color);
};

go(myCarMake, myCarYear, myCarKgs, myCarColor);
go(yourCarMake, yourCarYear, yourCarKgs, myCarColor);
go(hisCarMake, hisCarKgs, hisCarKgs, hisCarColor);

Notice some of the properties of this code:

  • busness-logic code (the definition of car properties and the go method) is mixed with the client code (the part where we call go), we can not separate them, because client code refers to the global variables we created (like myCarMake)
  • many repetitions (like CarMake is written 6 times) and it looks messy
  • easy to make the error (there are errors in last two calls)
  • hard to maintain - if we add a new parameter for the car, we will have to go over all the code base

Now the version with object (to simplify the code, I keep it as a simple object and separate constructor function, see this for other ways to define the object):

var CarPrototype = { // empty object-prototype
    make: "",
    year: 0,
    kilograms: 0,
    color: "",
    go: function() {
        alert(this.make + " " + this.kgs + " " + this.year + " " this.color);
    }
};

// function that constructs the new object
function createCar(make, kgs, year, color) {
    var car = Object.create(CarPrototype);
    car.make = make;
    car.kgs = kgs;
    car.year = year;
    car.color = color;
    return car;
}

var myCar = createCar("Chevy", 2004, 2000, "blue");
var yourCar = createCar("Ford", 2001, 1990, "white");
var hisCar = createCar("Ferrari", 2011, 2990, "red");

myCar.go();
yourCar.go();
hisCar.go();

And some properties of this code:

  • the business logic is clearly defined and separated from the client code, client code is not aware of internal structure of the car object
  • less repetitions and in general the code looks clearer
  • once the object is defined, it is really complex to make the error (you just do myCar->go(), no parameters passing, no chance to mix them or pass in the wrong order
  • easier to modify (if we add a new property for the car, the myCar.go() calls in client code don't need to be changed)

For sure, I didn't mention all the reasons to use OOP over PP, but I know this from practice - try to spend some time learning OOP principles and start using it and you'll see the huge improvement in code structure. You will make mistakes and do things wrong in the beginning, but the result will anyway be better than procedural code.

The general approach here is more important than specific language constructs, you can use same principles in C (which is not an OOP language) and in javascript (which has a specific OOP support) as well as in other languages.

I don't know a resource which compares PP and OOP side by side, but I think you don't really need it. Just look at examples of OOP applications and imagine how these would look like if written in procedural style.

My favorite book about OOP is Design Patterns, it demonstrates how elegant and powerful can be interaction between objects. Probably, you will also need to find something where basic OOP concepts are explained (already mentioned encapsulation, polymorphism, inheritance) and principles (most notably SOLID).

Borys Serebrov
  • 15,636
  • 2
  • 38
  • 54
  • When you say "busness-logic code (the definition of car properties and the go method) is mixed with the client code (the part where we call go), we can not separate them, because client code refers to the global variables we created (like myCarMake)" . I think you could have the "go() funtion declaration" in other file right? , that should resolve the "separation" part i guess. – Al Xx May 13 '22 at 13:20
1

They say OOP is about inheritance, polymorphism and encapsulation.

We will leave encapsulation aside, as it has nothing to do with OOP, but rather with modules. (At last, modules are the part of the language!)

Inheritance is a powerful concept that makes possible to reuse both logic and data. Extending your car example, we can do the following.

var AbstractCar = {
    go: function () {
        alert('go ' + this.make+ '!');
    }
};
var MyCar = Object.create(AbstractCar, {
    make: { value: 'Chevy', writable: true }
});

var YourCar = Object.create(AbstractCar, {
    make: { value: 'Mustang', writable: true }
});

MyCar.go(); // go Chevy!
YourCar.go() // go Mustang!

Polymorphism, on the other hand, allows you to treat different kinds of objects as one, as long as they conforms to the interface. Or, in the other words, as long as they can do want we want them to do.

For example, we need a string representation for out objects. Anything, that has a toString method could be concatenated with a string.

var myCar = {
    make: "Chevy",
    year: 2004,
    toString: function () {
        return this.make + ' ' + this.year;
    }
};

var statement = 'I want to sell ' + myCar;

console.log(statement); // I want to sell Chevy 2004

And that's really it.

OOP is not some superior technique that should be mastered for what its worth. Though it can pretty handy. : )