Facts.
Fact: JavaScript is an object oriented language.
Fact: JavaScript does not have classes.
Fact: JavaScript is dynamic and expressive enough to create structures that emulate classes.
Beyond those three facts, everything else here will be theory or opinion.
Expansion of facts:
JavaScript is an object oriented language but it does not have classes. Some people see these two statements as conflicting, but object oriented does not necessarily equal classes. Classes are only one paradigm used in object oriented systems, one of the more common ones, yes, but not the only one. Object oriented merely means that you have objects which contain both data and behavior – or properties and methods if you like.
You can create structures in JavaScript that emulate classes. Yes, but should you? That’s today’s topic.
First of all, why do people try to create class-like structures in JavaScript? I think mainly because they are coming from some other class-based language and that’s what they are used to. It makes them feel at home and transforms JS into something they think they can understand better. But there are many drawbacks to such an approach. Read a couple of in-depth articles or books on the subject. It starts out simple. You create a constructor function and add methods to the prototype. No real problem there. But then you want to implement inheritance and things begin to get tricky. Every fix has various drawbacks which have more fixes. The code ends up being quite complex, especially when you think that the main benefit is to make the programmer more comfortable with the language by making it seem like another language. Complexity has various potential side effects, including code brittleness, learning curve, file size, performance, and memory use, to name a few.
But beyond the idea of programmer comfort, there are some valid problems that people are trying to solve using classes. In most languages, classes offer 3 main benefits:
1. Code re-use via inheritance.
2. Information hiding / encapsulation.
3. Data typing and polymorphism.
These are all very fine things to strive for in programming anything. Let’s look at how they apply.
1. Code re-use.
This means you’re not typing the same code more than once. The DRY principle. Say you have a sprite system where each sprite has an image it draws to a canvas in a draw method:
[php lang=”js”]
function render() {
this.canvas.draw( this.image, this.x, this.y );
}
[/php]
And you have a player object that has certain behavior and an enemy object that has certain behavior, but both need to render to the canvas. It would be a poor decision for each of them to have a duplicate render function. You would somehow want to define that function once and have both of them share it. Then, if you ever needed to change it, you’d only need to change it once, they’d never get out of sync, you wouldn’t find errors creep into one, but not another, etc. And futures sprite types could also share the same render method.
In a class based system, you’d have a base class where any common code would live. Say a sprite class, with the render method. Then both player and enemy could inherit from sprite and both get the single render method. This is what everyone is taught in week one of most computer courses that venture into OOP. But remember, the problem we are trying to solve is code re-use. Class-based inheritance is not the only way, or even necessarily the best way to reuse code. In fact, another key principle often espoused in software design is “favor composition over inheritance”.
2. Information hiding and encapsulation.
This is all about setting up a public interface or api for an object that other objects can use, and having an internal implementation that other objects cannot (or should not) use. This means that you should be able to change the internal implementation without breaking any code that is using the public facing api. In class base languages, this is usually done with access modifiers such as public, private, protected, etc. Some languages have additional modifiers such as internal, friend, etc. and the actual rights that these modifiers bestow or block can vary slightly from language to language.
JavaScript does not have any native access modifiers, but there are ways of using closures to create functions and properties that are only accessible by methods of an object and not by outside code, thus accomplishing information and encapsulation. Interestingly, though, many pseudo-class systems in JavaScript do not even attempt to implement privacy, whereas other non-class based systems, such as modules, do this as a matter of course.
3. Data typing and polymorphism.
This says that if I create a class called Dog in a strictly typed system, I can make a variable that only accepts instances of the Dog class. And I can make a function that only accepts an argument of the Dog class. Furthermore, I can make a class called Animal and classes Dog and Cat that extend Animal. Now I can have a variable or method parameter that states it only accepts Animal objects, to which I can pass either Dogs or Cats.
Since JavaScript is not a strictly typed language, this concept is almost completely useless. I cannot specify what type my variables should be, nor can I state what kind of object a method parameter can accept. Yes, I could code all my methods with checks to typeof or instanceof and throw runtime errors if the wrong type was passed, but … nah.
Summary.
So, what it comes down to in most cases of people creating these complex class emulation systems, is code reuse. A valid concern, but not one that I am convinced has pseudo-class-based inheritance as its best answer. In future posts, I’ll cover some of the different ways to accomplish code re-use as well as encapsulation in JavaScript.
Maybe it’s nice to cover the Object.create() method and why it’s a ‘true” prototype based inheritance (using another object as prototype), and so consptually slightly different from the way you can do it using constructor-functions and the ‘new’ keyword. It took me a while to grasp the differences and why/when those are usefull.
Yup, that’s the plan. Object.create and some other common patterns.
Your “facts” are really only facts according to your own definition of what qualifies a language as supporting OOP, otherwise you wouldn’t be saying things like “expressive enough”. It’s compter science, not computer poetry, so spare us the artistic license. If you’re going to use a term, you don’t get to reinvent it, make up one of your own.
If you apply generally accepted definitions of OOP fundamentals, JavaScript fails to make the grade. Can you effecively apply OO principles in JavaScript? Sure, but don’t try to make it into something it’s not, or tell people who may not know any better that it’s something it’s not, you’re not helping them.
I knew I’d get flak for using the term “expressive”, but it’s a valid computer science term. http://en.wikipedia.org/wiki/Expressive_power
Very simply, JavaScript, although it does not have classes, has constructs that allow you to express the idea of classes. There’s no artistic license or poetry involved, so relax, eh?
What accepted definitions of OOP fundamentals do not apply? I’m not saying that JS is the richest OOP language out there, but there is no doubt it is classified as an object oriented language.
Rather than taking artistic license, I think it’s more that Keith was being polite. Put another way, being “expressive enough” to emulate classes means that if someone is too stubborn not to embrace prototypal OOP, he or she can force JavaScript to act in a less natural way than it’s intended.
In short, though, classes aren’t required for OOP, and you should probably brush up on those fundamentals so that you will stop telling people who may not know any better that prototypes aren’t OOP because you’re not helping them.
Why is there always someone that comes along and ruins the mood? There are lots of ways to say your piece without being rude Tim.
I think I have to start using CoffeeScript to retain my sanity.
JavaScript has proven itself poorly as it has needed to mature. It still doesn’t have great tools, it doesn’t scale very well, and it’s difficult to create coding-standards around (which can be seen in the necessity of this blog post).
JS doesn’t have to be the language that we use for the rest of our lives. It has served a wonderful purpose, but I hope it’s time is coming to an end in favor of a solution such as Google’s Dart.
With Dart, imagine if every code sample someone posted on their blog was editable and compile-able right in the blog post. We’d have optional strict-typing (great for prototyping AND for production code).
I think you know something’s wrong when Google–JavaScript’s greatest proponent–sees enough of an inherent problem with JS to want to replace it. Once you try to build enterprise-level applications with it (like Google does), it just doesn’t cut it.
Great post. The data typing issue I think often goes hand in hand with the idea of living without a compiler. In my past I did ColdFusion and there was a long debate on the usefulness of type errors in a runtime language. The outcome in either case is actually identical (i.e. a runtime error) with the only difference being the error type and text of the error message.
I think the main reason to use oop in JS is to organize where your code into logical locations and choke points. I think sometimes when we say ‘code reuse’ it gives the wrong impression – it isn’t that you have to create less code at design time, it is that the runtime always uses the same code for the same functionality. That makes debugging, testing, and iterating much easier, and eventually things can migrate into a framework.
There are other ways of organizing JS code (though not sure if “bag of functions” is an actual technique ; ), but they tend to be home made, less portable. Logical structure and choke points are 90% of it though, so whatever works for people.
I agree with the general thrust here, that the benefits of emulatinf inheritance in JS are limited, and the implementations are hacky.
But, if you look at something like EaselJS, you see JS inheritence used to perfection. So it certainly has a role and can work well.
@isaacs wrote a really good post that explainsthe evolution of a prototypal language user.
Nowadays I mix lots of different patterns but one of the things that most improved my workflow and code structure was using RequireJS, I wasn’t really missing “classes” but just a sane way to organize my code into reusable modules and to manage dependencies…
A really good book about JavaScript for people that are coming from other languages is JavaScript Patterns by Stoyan Stefanov, explains the core concepts of the language, how to avoid common pitfalls and how to structure your code.
cheers.
That’s a great book. This also goes into many of the same topics: http://www.amazon.com/Test-Driven-JavaScript-Development-Developers-Library/dp/0321683919/ref=sr_1_1?ie=UTF8&qid=1326307789&sr=8-1
I’ve also been getting into RequireJS and testing out various frameworks.
My most important use for strict typing is related to IDE’s. The coding speed of languages without clearly defined types is just a lot lower than the ones with clearly defined types. I am not necessarily focusing on classes here. BUT: Having a way to actually get in-IDE documentation for the used method and the required parameters is A LOT more important to me than getting error messages before running code.
@Brian Rinaldi: There is a second – important – difference between compile-time errors and runtime errors: All compile-time errors occur on compiletime while runtime errors occur during tests. I argue that this is some sort of automated test system (that you could provide by running similar type checks in written unit tests before starting an application.
That is so true, at Aptana we were making great progress on docApi and intellisense, but then you run up against things that just aren’t computable because they rely on knowing what code has run at compile time, among other things. They are more common that I thought they would be, in real code, in common libraries etc. Intelllisense became intelliguess, and reasonable guesses started taking too long to compute.
It also doesn’t help that everyone has their own way of writing ‘good’ Javascript. Some of the most common libraries defy reasonable analysis with tools – eg JQuery, Mochikit etc. They bend JS into something else – more ‘normal’ oop, more Functional, more encapsulated etc. It’s great you can do that if the language allows the tools to keep up, but in this case it doesn’t, so it is a pretty steep price.
I think Google has the right idea here, use JS for small things, and use a stricter toolable language for larger things, and pay the price of transcoding it. I doubt any new language from a large company will replace JS, there are too many vested interests (although most browsers are now just webkit with a UI, so you never know). Maybe a language like Haxe that doesn’t have vested corporate interests that would be vetoed by competitors.
Great insight, thanks for sharing! This gives an answers on why there are no real tools for JS.
JavaScript objects are not polymorphic, they are typeless and completely interchangeable. While that allows behavior that appears to be polymorphics it’s only by accident and doesn’t provide any of the benefits of a real object taxonomy.
Encapsulation is impossible in JavaScript, if I can get my hands on an object I can probe it and know all its secrets. If all calling code plays nicely you can fake encapsulation but you can’t enforce it.
There’s more, but my point was that everyone wants their favorite tool to be trendy and cool and object oriented rather than just accepting it for what it is.
Stop overloading [sic] “object oriented”
“JavaScript objects are not polymorphic” – absolutely. Exactly what I said in the post.
“Encapsulation is impossible in JavaScript” – for a simple object, exactly. Also said that in the post. But there are ways to completely hide data in closures. Also referred to that in the post.
Polymorphism and encapsulation are not definitions or requirements of object oriented. They are attributes of some, but not all, object oriented languages.
It’s not a matter of being trendy or overloading definitions. It’s just a matter of knowing the definition.
OOP is just organizing your code into elements that have data and functions relevant to it in one package. After that it is just techniques to milk the advantages of that approach. Certainly the majority of JS in browsers isn’t object oriented, but the prototype object is for exactly that, so it is an object oriented language.
You may find oop less useful if it isn’t locked down, and I agree, but better than semantics or regrets, why not dwell on ‘what is the best approach to scaling in a non locked down oop language’, or ‘I know the costs, what are the benefits’..
JS is the porn of computer languages – immoral, embarrassing, a poor substitute for the real thing, yet popular and kind of fun (for the first little while).
I’m just surprised that out of all that I said in the post, the point of whether or not JS is or is not object oriented is the only thing anyone is arguing over… 🙂
Object Oriented: http://en.wikipedia.org/wiki/Object-oriented_programming
Prototype-based Programming: http://en.wikipedia.org/wiki/Prototype-based_programming
JavaScript: http://en.wikipedia.org/wiki/JavaScript
I believe more that the #1 reason I must emulate classes is because encapsulation helps me get things done, neatly and one at a time
Keith,
You should spend some time looking at the google closure way of doing things. You have inheritance, interface and type checking through a compiler. The compiler optimises one semantic to more efficient javascript. The results are pretty incredible in terms of optimisations. It even supports the concepts of modules which load in over time and there are a lot components.
Looking forward to more of these posts. I’m all over the place with Javascript right now. I need some order!
Thank you! ^ 1000
Can’t wait to read more.
“OOP to me means only messaging, local retention and protection and
hiding of state-process, and extreme late-binding of all things. It
can be done in Smalltalk and in LISP. There are possibly other
systems in which this is possible, but I’m not aware of them.”
Alan Kay