48

When I first started programming Javascript after primarily dealing with OOP in context of class-based languages, I was left confused as to why prototype-based OOP would ever be preferred to class-based OOP.

  1. What are the structural advantages to using prototype-based OOP, if any? (e.g. Would we expect it to be faster or less memory intensive in certain applications?)
  2. What are the advantages from a coder's perspective? (e.g. Is it easier to code certain applications or extend other people's code using prototyping?)

Please don't look at this question as a question about Javascript in particular (which has had many faults over the years that are completely unrelated to prototyping). Instead, please look at it in context of the theoretical advantages of prototyping vs classes.

Thank you.

1

3 Answers 3

48

I had quite a lot of experience of the both approaches when writing an RPG game in Java. Originally I wrote the whole game using class-based OOP, but eventually realised that this was the wrong approach (it was becoming unmaintainable as the class hierarchy expanded). I therefore converted the whole code base to prototype-based code. The result was much better and easier to manage.

Source code here if you are interested (Tyrant - Java Roguelike)

Here are the main benefits:

  • It's trivial to create new "classes" - just copy the prototype and change a couple of properties and voila... new class. I used this to define new type of potion for example in 3-6 lines of Java each. Much better than a new class file and loads of boilerplate!
  • It's possible to build and maintain extremely large numbers of "classes" with comparatively little code - Tyrant for example had something like 3000 different prototypes with only about 42,000 lines of code total. That's pretty amazing for Java!
  • Multiple inheritance is easy - you just copy a subset of the properties from one prototype and paste them over the properties in another prototype. In an RPG for example, you might want a "steel golem" to have some of the properties of a "steel object" and some of the properties of a "golem" and some of the properties of an "unintelligent monster". Easy with prototypes, try doing that with an inheritance heirarchy......
  • You can do clever things with property modifiers - by putting clever logic in the generic "read property" method, you can implement various modifiers. For example, it was easy to define a magic ring that added +2 strength to whoever was wearing it. The logic for this was in the ring object, not in the "read strength" method, so you avoided having to put lots of conditional tests elsewhere in your code base (e.g. "is the character wearing a ring of strength increase?")
  • Instances can become templates for other instances - e.g. if you want to "clone" an object it is easy, just use the existing object as the prototype for the new object. No need to write lots of complex cloning logic for different classes.
  • It's quite easy to change behaviour at runtime - i.e. you can change an properties and "morph" an object pretty much arbitrarily at runtime. Allows for cool in-game effects, and if you couple this with a "scripting language" then pretty much anything is possible at runtime.
  • It's more suited to a "functional" style of programming - you tend to find yourself writing lots of functions that analyse objects an act appropriately, rather than embedded logic in methods attached to specific classes. I personally prefer this FP style.

Here are the main drawbacks:

  • You lose the assurances of static typing - since you are effectively creating a dynamic object system. This tends to mean that you need to write more tests to ensure behaviour is correct and that objects are of the right "kind"
  • There is some performance overhead - since reads of object properties are generally forced to go through one or more map lookups, you pay a slight cost in terms of performance. In my case it wasn't a problem, but it could be an issue in some cases (e.g. a 3D FPS with a lot of objects being queried in every frame)
  • Refactorings don't work the same way - in a prototype based system you are essentially "building" your inheritance heirarchy with code. IDEs / refactoring tools can't really help you since they can't grok your approach. I never found this a problem, but it could get out of hand if you are not careful. You probably want tests to check that your inheritance hierarchy is being constructed correctly!
  • It's a bit alien - people used to a conventional OOP style may easily get confused. "What do you mean there's only one class called "Thing"?!?" - "How do I extend this final Thing class!?!" - "You are violating OOP principles!!!" - "It's wrong to have all these static functions that act on any kind of object!?!?"

Finally some implementation notes:

  • I used a Java HashMap for properties and a "parent" pointer for the prototype. This worked fine but had the following downsides: a) property reads sometimes had to trace back through a long parent chain, hurting performance b) if you mutated a parent prototype, the change would affect all children that had not overriden the changing property. This can cause subtle bugs if you are not careful!
  • If I was doing this again, I would use an immutable persistent map for the properties (kind of like Clojure's persistent maps), or my own Java persistent hash map implementation. Then you would get the benefit of cheap copying / changes coupled with immutable behaviour and you wouldn't need to permanently link objects to their parents.
  • You can have fun if you embed functions / methods into object properties. The hack that I used in Java for this (anonymous subtypes of a "Script" class) wasn't very elegant - if doing this again I'd probably use a proper easily-embeddable language for scripts (Clojure or Groovy)
5
  • 5
    +1 eerily similar to steve-yegge.blogspot.com/2008/10/universal-design-pattern.html
    – jk.
    Sep 27, 2011 at 8:45
  • (+1) Its a good analysis. Altought, its more based in Java model, for example, Delphi, C#, VB.Net has explicit properties.
    – umlcat
    Sep 27, 2011 at 15:22
  • 3
    @umlcat - I feel the Java model is pretty much the same as the Delphi/C# model (apart from the nice syntactic sugar for property access) - you still have to statically declare the properties that you want in your class definition. The point of a prototype model is that this definition is not static and you don't have to make any declarations in advance....
    – mikera
    Sep 28, 2011 at 2:21
  • This missed a big one. You can alter the prototype, which effectively alters properties on every single instance, even after they're created without touching the constructor of the prototype. Jun 13, 2013 at 4:38
  • Regarding to multiple inheritance being easier, you compared it with Java which doesn't support multiple inheritance, but is it easier compared with languages that support it such as C++?
    – Piovezan
    Jan 14, 2016 at 21:39
2

The main advantage of prototype-based OOP its that objects & "classes" can be extended at runtime.

In Class-based OOP, there are several good features, unfortunately, it depends on the programming language.

Object Pascal (Delphi), VB.Net & C# has a very direct way to use properties (not to be confused with fields) and properties' access methods, while Java & C++, properties are accessed by methods. And PHP has a mixture of both, called "magic methods".

There are some dynamic typing classes, although the main class O.O. languages have static typing. I think static typing with Class O.O. its very useful, because allows a feature called Object Introspection that allows to make those I.D.E. and develop web pages, visually, and quickly.

0

I have to agree with @umlcat. Class extension is a major advantage. For example, suppose you want to add more functionality to a string class over a long period of time. In C++ you would do this through continued inheritance of previous string class generations. The problem with this approach is that each generation essentially becomes a different type of it's own which can lead to massive rewriting of existing code bases. With prototypical inheritance you simply 'attach' a new method to the original base class ... , no massive build up inherited classes and inheritance relationships everywhere. I would love to see C++ come up with a similar extension mechanism in their new standard. But their committee seems to be ran by people who want to add the catchy and popular features.

1
  • 1
    You might want to read Monoliths "Unstrung", std::string already has too many members which should be independent algorithms or at least non-friend non-members. And anyway, new member-functions can be added without changing the in-memory-layout any if you can modify the original class. May 23, 2018 at 15:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.