Do I need “private” properties of objects in Javascript?
Recently in many articles (on Habré and not only) I often see examples of emulation of private properties of objects in JS through closures. Authors usually explain this by their desire to use such an OOP mechanism as encapsulation , and thereby guarantee work with an object exclusively through its methods, without directly affecting properties.
In this article, I propose to objectively consider the advantages and disadvantages of this approach, so that everyone can decide for themselves whether to use it or not.
So, for starters, the subject of discussion. We will compare the creation of objects using the “constructor - prototype” link (that is, all methods are stored in the prototype, all properties are created by the constructor)
and emulating private properties (all properties of the object are variables of the constructor function, all methods that work with these properties are created directly in the constructor to make the closure work).
Later in the article I will call them, respectively, “private” and “prototype”, for short.
The main advantage (and the main reason for applying) of the private method is the “hard” encapsulation of objects. Tough in the sense that it restricts access to properties directly, not only ideologically, at the level of recommendations, but also in fact.
Also, the pluses include saving on the word this when accessing properties in methods (property = 5 instead of this.property = 5, for example).
They can be divided into several groups.
Ideology
Performance
Private properties in all respects slow down the script, in comparison with conventional properties. Objects with private properties (or methods) take longer to create and take up more space in memory (because each time an object is created, its methods are re-created and each copy of the method takes its place in memory). I conducted a simple test, creating 1000 objects with 15 methods, first in a “private” way and then in a “prototype” way. In the first case, IE6 spent 250ms on the task, IE7 - 110ms. In the second case, they both spent 15ms each.
The difference of 100-200ms may seem insignificant, however, at the moment the animation is displayed on the page or just during some user action, it is already perceived very negatively (because the whole page freezes, and if the browser does not use tab sharing , then the whole browser).
In addition, the application has the ability to become more complex over time, objects are overgrown with new methods. Having decided to expand our facilities with 3-4 more methods, with the private method we will get another + 25-50ms extra time that will be spent on their creation.
In the case of prototypes, we can add at least 50 new methods - this will not affect the creation of objects at all (as well as memory consumption).
Functionality
Support and code change
Understand me correctly, I am not against using closures, accessors, or encapsulation; I am against using curves, inferior methods for this, which in any case do not solve the task 100%, but only complicate the code and make it harder for the interpreter.
All of the above problems can be avoided by using an underscore (or something like that) to denote “private” properties, if one really wants to distinguish access to them ideologically.
It’s also worth considering that when someone (or you yourself) really want to access the property directly, it most likely indicates that either its accessors are not convenient for use, or that they are not at all needed (for example, there is no need to validate the input value when writing a property or to perform some other additional actions).
In this article, I propose to objectively consider the advantages and disadvantages of this approach, so that everyone can decide for themselves whether to use it or not.
So, for starters, the subject of discussion. We will compare the creation of objects using the “constructor - prototype” link (that is, all methods are stored in the prototype, all properties are created by the constructor)
function make_obj(a, b) {
this.prop1 = a;
this.prop2 = b;
}
make_obj.prototype = {
method1: function(){...},
method2: function(){...},
methodN: ...
}
* This source code was highlighted with Source Code Highlighter.
and emulating private properties (all properties of the object are variables of the constructor function, all methods that work with these properties are created directly in the constructor to make the closure work).
function make_obj(a, b) {
var prop1 = a;
var prop2 = b;
method1 = function(){...}
method2 = function(){...}
methodN = ...
}
* This source code was highlighted with Source Code Highlighter.
Later in the article I will call them, respectively, “private” and “prototype”, for short.
The main advantage (and the main reason for applying) of the private method is the “hard” encapsulation of objects. Tough in the sense that it restricts access to properties directly, not only ideologically, at the level of recommendations, but also in fact.
Also, the pluses include saving on the word this when accessing properties in methods (property = 5 instead of this.property = 5, for example).
disadvantages
They can be divided into several groups.
Ideology
- You can often hear a statement that if a programming language does not have such things as abstraction, polymorphism, inheritance, and encapsulation, then it is not an OOP language at all. However, many forget that the presence of these concepts is just a feature of the implementation of OOP in certain languages, and not at all dogma.
- JavaScript, JScript and the ECMA-262 standard based on them do not provide any “native”, native means for hiding object properties or methods from being accessed from outside. All properties of objects created within the program are available for reading and changing. It directly follows from this that any implementation of "private" properties is an add-on over the language, entailing additional overhead costs for code execution and its support. More on this in the following paragraphs.
Performance
Private properties in all respects slow down the script, in comparison with conventional properties. Objects with private properties (or methods) take longer to create and take up more space in memory (because each time an object is created, its methods are re-created and each copy of the method takes its place in memory). I conducted a simple test, creating 1000 objects with 15 methods, first in a “private” way and then in a “prototype” way. In the first case, IE6 spent 250ms on the task, IE7 - 110ms. In the second case, they both spent 15ms each.
The difference of 100-200ms may seem insignificant, however, at the moment the animation is displayed on the page or just during some user action, it is already perceived very negatively (because the whole page freezes, and if the browser does not use tab sharing , then the whole browser).
In addition, the application has the ability to become more complex over time, objects are overgrown with new methods. Having decided to expand our facilities with 3-4 more methods, with the private method we will get another + 25-50ms extra time that will be spent on their creation.
In the case of prototypes, we can add at least 50 new methods - this will not affect the creation of objects at all (as well as memory consumption).
Functionality
- Although using the “private” approach, you can emulate the private attribute for properties and methods, you cannot set the protected attribute in its classic form (that is, inaccessibility from the outside, but accessibility for posterity).
This greatly limits the possibilities of inheritance, as the child can neither access the properties of the parent directly, nor override its public methods (similarly, the connection with the properties will be lost). - It is impossible to access the property of an object using a string literal, for example like this [[a> 2? 'Max': 'min')]
It would seem a trifle, but unpleasant.
Support and code change
- Objects with private properties are more difficult to debug. In order to view the internal state of such an object, you must either iterate over all its getters or create a special dump function in its constructor (moreover, you will have to copy it so that it has access to the properties).
Needless to say, in the case of ordinary objects, the question of displaying a dump is not worth it at all. - In a language such as PHP5, we can, during development, easily change the private property to public, or public to protected - and the level of access to the property or method will immediately change.
When emulating private properties in JS, we will have to add or remove this everywhere in the code . before accessing a property.
Understand me correctly, I am not against using closures, accessors, or encapsulation; I am against using curves, inferior methods for this, which in any case do not solve the task 100%, but only complicate the code and make it harder for the interpreter.
All of the above problems can be avoided by using an underscore (or something like that) to denote “private” properties, if one really wants to distinguish access to them ideologically.
It’s also worth considering that when someone (or you yourself) really want to access the property directly, it most likely indicates that either its accessors are not convenient for use, or that they are not at all needed (for example, there is no need to validate the input value when writing a property or to perform some other additional actions).