DEV Community

Pacharapol Withayasakpunt
Pacharapol Withayasakpunt

Posted on

How is JavaScript class / prototype different from Python / Java?

And if you care,

  • What about object copying / mixins?
  • How does prototype work in TypeScript?

Why should I care about explicit private fields anyway, as I can always prefix it with underscore (_) just like Python, Dart or Go?

Top comments (4)

Collapse
 
ahferroin7 profile image
Austin S. Hemmelgarn

A couple of big differences that come to mind:

  • Looking at how methods resolve is a bit easier in JS than in many class-based languages, because you have a fixed single chain to follow for method resolution that is inherently visible as a property of the object itself. In some class-based languages, things are not too horrible, and may even be pretty similar to this, but a lot require special functions just to run the MRO logic to figure out what would get called, while in JS it really is just a simple walk through a chain of objects.
  • In JS, you can actually rather easily have anonymous classes, provided you use the ES6 class syntax. This works because an ES6 class expression evaluates to the constructor function for that 'class', and thus you can use it any way you could use a regular function (provided you remember the new keyword when you want to instantiate it. This lets you do some rather interesting things, such as defining and instantiating a class as a single expression without having to bind the class to a name.
  • In JS, scoping of fields is actually enforced. Private fields stay private, period. You don't have to rely on people using your code following conventions to ensure that they won't muck about with your class internal stuff. This got a bit interesting recently, as ES2019 (which is not widely implemented yet) added the ability to actually define fields as private in ES6 style class definitions, and they behave in this case much like conventional languages do. Without that though, you actually can't even access private fields from methods added after the class definition, only from instance methods put on the object itself by the constructor (because 'private' fields without the ES2019 syntax are just variables in the context of the constructor, and you need closures within the constructor to access them).
Collapse
 
rhymes profile image
rhymes

Not sure I can touch on all the topics, going to ignore Typescript because I don't know it.

JS is an OOP language with prototype based inheritance which essentially means that the template to create a new object is another object. @lydiahallie wrote a great visual explanation on how that works: JavaScript Visualized: Prototypal Inheritance.

Java and Python also are object oriented but they implement a different paradigm, class based inheritance, which essentially means objects are printed from a class which acts as their template. Class based inheritance allows multiple inheritance, which Python offers.

Mixins are a good way to share behavior between objects, they could be viewed as a way to bypass the limitations of single class inheritance.

Not to say that multiple inheritance is the perfect solution, it can get complicated fast. The more I program the more I prefer to keep classes simple and rely on composition and dependency injection to create objects with the various bits of shared behavior.

Not everyone agrees, Ruby has mixins that in a real world app can quickly become similar to the issues that multiple inheritance has, the simplest being: "where does this method call I'm seeing in this line come from?". The same can happen with open classes. JS has a similar potential behavior with the fact that you can add methods to objects or their prototype at runtime.

None of this is inherently a problem, it's quite a perk of these dynamic languages, it becomes an issue if devs don't have a bit of "discipline" in how they extend behavior.

And with this we circle back to your other concern, encapsulation. My belief is that essentially Python is built with an unspoken contract in which the creator of the class trusts the consumer of the class because they are "both adults" and if the latter wants to delve into the private methods or private state it's their own responsibility.

So Python trusts devs, Java is on the opposite side of the spectrum in this. The agreement is that the consumer of the class shall always stick with the public interface of the class. The only drawback, which happened to me in the past, is that if the implementor made a mistake in the design of the class, the consumer essentially has to wait until the new version is available, whereas with Python you write code to bypass the mistake and wait for the new version to remove the patch.

Hope this was helpful to understand the different philosophies.

Collapse
 
jankapunkt profile image
Jan Küster

Hope this example is useful: Prototype based inheritance works by references to a single definition of class members (Class.prototype.someFunction) in the prototype class while in Java you create a copy of the members when inheriting from a class.

This has pros and cons on both sides and it would open the gates of hell discussing them now in-depth :-)

Collapse
 
chrispardy profile image
chris-pardy

First to address TypeScript, there is no difference between TypeScript and JavaScript as far as classes are concerned. TypeScript supports "private" fields on classes, but it's more a suggestion (like the underscore prefix) than something enforced. In general the advantage of enforcing private fields, meaning that the field cannot be read or written to, is that you can rely on that field only changing in ways that your class specifies.

To understand the difference between class based and prototype based inheritance it's best to think of how we resolve a field/method.

In class based inheritance an object has fields that are specific to that instance and a class, the class has every field/method that is shared by all instances of this class. This includes all the fields on the classes parent class and the parent's class parent class etc. It's possible to do this because classes declare their parent classes, and those declarations don't change. Now to resolve a field on an object we first check the object, then the class, then we're done.

In prototype based inheritance an object has fields that are specific to that instance and a prototype, which is an object. Because a prototype is an object it has fields and a prototype. To resolve a field you first check if it's defined in the object, then check if it's defined in the prototype, then check the prototype's prototype, etc.

Given the nature of prototype inheritance it's potentially much slower than class based inheritance, however you get a trade-off in flexibility. For instance switching the prototype of an object after it's been created is trivial in JavaScript and effectively impossible in Java. You can also make changes arbitrarily in the prototype chain, which is powerful but also scary.

One note is that the ES6 classes have a syntax that makes then look a lot like classes in Java or Python but they are not. It's just a new syntax over the old functionality.

Not going to go into mixins, as others have explained they're a way to inherit from 2 different classes, how they're implemented is largely language specific.