Why the definition of properties in the prototype is considered antipattern

I often see this template define javascript objects

function Person(name) { this.name = name; } Person.prototype.describe = function () { return "Person called "+this.name; }; 

And this article says that adding properties directly to the objct prototype is considered an anti-pattern.

Based on languages ​​based on the “classic class”, defining properties other than methods doesn’t sound quite right, especially in javascript, where the method should just be a property with a function value (am I here?)

I wanted to know if anyone could explain this, or even suggest a better way to handle these situations.

+6
source share
5 answers

In ordinary object-oriented languages, you have a class definition that describes members, methods, and constructor.

In JS, the definition of "class" (this is not like a class, as in other languages ​​... sometimes the term pseudo-class is used) - the constructor itself. If your object is parameterized with name , it makes sense to write

 function Person(name) { this.name = name; } 

i.e. The name property must be set in the constructor.

Of course you can write

 function Person(name) { this.name = name; this.describe = function() { ... }; } 

and it will work as you expect.

However, in this case, you create a separate instance of the method each time the constructor is called.

On the other hand, here:

 Person.prototype.describe = function () { return "Person called "+this.name; }; 

you define a method only once. All instances of Person will receive a pointer (called __proto__ and not accessible by the programmer in most browsers) until Person.prototype . Therefore, if you call

 var myPerson = new Person(); myPerson.describe(); 

it will work because JS searches for object objects directly in the object, and then in its prototype, etc. all the way to Object.prototype .

The fact is that in the second case there will be only one instance of the function. Most likely, you will agree that this is the best design. And even if you do not, it just takes up less memory.

+6
source

There is nothing wrong with this code. This is supposed to mean:

 function Person(name) { this.name = name; } Person.prototype.age = 15; //<= adding a hardcoded property to the prototype 

Now you will see the following:

 var pete = new Person('Pete'), mary = new Person('Mary'); pete.age; //=> 15 mary.age //=> 15 

And most of the time, this is not what you want. The properties assigned to the prototype constructor are shared between all instances, the properties assigned inside the constructor ( this.name ) are instance specific.

+5
source

As Arksanas says, the article mentions data properties.

I believe the reason is that the data is usually instance specific , so it makes no sense to add it to the prototype.

In addition, if your data is of a mutable type, for example, an array, and you assign it to a prototype, then this instance of the array is shared among all instances, and you cannot use it as if each instance had its own array.


Example: The following are incorrect behavior:

 function Set() { } // shared between instances // each instance adds values to **the same** array Set.prototype.elements = []; Set.prototype.add = function(x) { this.elements.push(x); }; 

It should be:

 function Set() { // each instance gets its own array this.elements = []; } Set.prototype.add = function(x) { this.elements.push(x); }; 

Summarizing:

  • Add properties that should be passed to all prototype instances.
  • Assign specific instance data inside a constructor function.
+3
source

Just like Arksanas wrote in his commentary. The data properties in the prototype are more or less similar to class-level variables in traditional oop. And they are not used on a daily basis unless you have special needs. It's all.

+1
source

Declaring prototype properties is not an anti-pattern at all. When I look at the prototype object, I think: "this is what a prototype object of this type has for data and methods."

Others warn of the absence of reference value properties in the prototype, for example: Foo.prototype.bar = []; --- because arrays and objects are reference types. The type of reference is immutable, so each instance of a “class” refers to the same array or object. Just set them to null in the prototype, then give them a value in the constructor.

I always include all the properties in the prototype for one very understandable reason: communication with other programmers, which properties are publicly available and what are their default values, without having to sift them through the constructor in order to understand this.

This becomes especially useful if you are creating a shared library that requires documentation.

Consider the following example:

 /** * class Point * * A simple XY coordinate class * * new Point(x, y) * - x (Number): X coordinate * - y (Number): Y coordinate * * Creates a new Point object **/ function Point(x, y) { /** * Point#x -> Number * * The X or horizontal coordinate **/ this.x = x; /** * Point#y -> Number * * The Y or vertical coordinate **/ this.y = y; } Point.prototype = { constructor: Point, /** * Point#isAbove(other) -> bool * - other (Point): The point to compare this to * * Checks to see if this point is above another **/ isAbove: function(other) { return this.y > other.y; } }; 

(Documentation format: PDoc )

Just reading the documentation is a little awkward because information about the x and y properties is built into the constructor function. Compare this to the “anti-pattern" of incorporating these properties into the prototype:

 /** * class Point * * A simple XY coordinate class * * new Point(x, y) * - x (Number): X coordinate * - y (Number): Y coordinate * * Creates a new Point object **/ function Point(x, y) { this.x = x; this.y = y; } Point.prototype = { /** * Point#x -> Number * * The X or horizontal coordinate **/ x: 0, /** * Point#y -> Number * * The Y or vertical coordinate **/ y: 0, constructor: Point, /** * Point#isAbove(other) -> bool * - other (Point): The point to compare this to * * Checks to see if this point is above another **/ isAbove: function(other) { return this.y > other.y; } }; 

Now, looking at the prototype, you get a snapshot of a real object, which is much easier to visualize in your head, and easier for the author to write documentation. The constructor function is also not cluttered with documentation and is not related to animating a Point object.

A prototype has everything and is a canonical source of information about what a "prototype" Point object has for both methods and data.

I would say that not including the data properties in the prototype is an anti-pattern.

+1
source

Source: https://habr.com/ru/post/922593/


All Articles