What is Javascript Prototype?

Asked

Viewed 2,433 times

23

I see in various instances of native objects a '.protoype' in the middle before a method or attribute but I don’t know what they are EXACTLY.

The only thing I know is that it’s Javascript’s way of forming classless inheritances, only I can’t understand that concept and I can’t even use it.

I’ve read that answer > How prototypes work in Javascript?

Only I couldn’t understand 100%. Could explain me from a + easy point of view?

  • @Murilohlad, I only know JS so far, to learn.

  • Friend, I also do not master Javascript, maybe some concepts of POO (Object Oriented Programming) help you. Note that a Javascript function cannot be repeated again, with the use of prototypes you can make the 'Car' and a 'Person' have the 'Walk()' function in different ways (different returns and different ways of working).

2 answers

25

If you’ve read it the technical part and continues with doubts (and from what I understand has no experience with programming other than JS), I’m going to go down to the basics. I will try to be brief and not 100% accurate, touching only the relevant points for you to understand the prototype. OK?

(which doesn’t mean the answer will be short, despite my best efforts...)

Extending

A data structure (which I will simply call "object") representing a real-life entity - or only its relevant aspects to the application domain - needs to be defined. Your fields need to be named and depending on the typed language. The operations applicable to it need to be defined. All this is very laborious and often desirable if reuse code of one object in the other.

Reuse sometimes means compose - If you have a "motor" object, you can use it inside a "car" object and inside a "motorcycle" object, for example. Both conceptually and pragmatically it makes sense to say that "a car contains an engine".

Other times, reuse means extend - if you have a "monster" object representing a generic monster, with its attributes, behavior, etc., and want to create a "zombie" object with some more characteristics (or simply different) and another "vampire" object, it makes little sense conceptually (but maybe do in practice) that some fields and methods come from one object and others from another (ex.: vampiro.morder() vs vampiro.monstro.andar()). It is desired that you can treat the derived objects (or children) as if they possessed all that the base object (or parent) possesses - but without having to repeat the whole definition again.

There are several ways to extend an object, and each language does so in a way. The most common is the subtype - where the derivative type has an implicit copy of all fields of the base type, and all transactions of the base type are also applicable to the derivative type. Another is the mixin, where everything is copied (fields and methods). And another is the prototype, in which nothingness is copied, things are just delegates. Perhaps there are others, I don’t remember, the important thing is that to every extent the child object behaves as if it had everything the father has, and a few more things of itself.

Example of prototyping

Before I get into the language support of concept, let me explain the concept itself. First, let’s define the object that will be the prototype of other objects:

var monstro {
    tipo:"GameObject",
    movel:true,
    controlavel:false,
    hostil:true,
    x:0, y:0,
    andar:function(dx,dy) { this.x += dx; this.y += dy }
}

Note that monstro It’s an object, but we hardly want to have a single monster on the set, right? We might want to have N monsters, but they would all have all the features in common except for the position in the scene. Let’s then create a second monster - also generic - using the copy method:

var monstro2 = { x:10, y:-10 };
for ( var p in monstro )
    if ( monstro2[p] === undefined )
        monstro2[p] = monstro[p];

monstro2.hostil; // true
monstro2.andar(5, 5); // monstro2.x = 15, monstro2.y = -5

This causes a second monster to be created, identical to the first, where only its position is different. However, this only occurs at the time of copying (in addition to wasting memory for no reason). As I will show below, Javascript allows this to be done in a valid way for the entire execution of the program.

The same method could be used to create a different, more "specialized monster":

var zumbi = { x:100, y:0,
    fala:"braaaains",
    morder:function() { ... }
}
for ( var p in monstro ) ...

zumbi.hostil; // true
zumbi.andar(1,3);
zumbi.morder();

etc. The key point is that the prototype serves as a "template" for the creation of other objects, so that in the end each object is (or at least seems to be) independent of its prototype - similar, yes, but a separate object with no relation to the father. Its interface, or API, is that it has things in common (more, perhaps, but preferably never less).

For this example, I used a simple copy right after creation. But when using the language’s prototyping feature, one can maintain this relationship between the parent and child objects throughout the program’s run, and without making any copies. The end effect is the same (the child object inherits the parent API), but it updates and maintains, and is more efficient.


In your question you also mention the .prototype, and in fact it is one of the most confusing aspects of Javascript. To understand it, it is necessary to first look a little "underneath the cloths":

"Special fields"

In a "primitive" or "raw" programming language, an object knows nothing about itself. It has only data and nothing else:

struct MeuStruct {
    int a;
    float b;
    char* c;
}

void* objeto = malloc(sizeof(MeuStruct)); // Apenas para exemplo, ninguém programa assim
  • How to find the type of the object? You don’t discover...
  • How to access his fields by name? You do not access...
  • How do you "inherit" from this object? You do not inherit...

Only if you know that an object is of a certain type is that you can at compilation time have some more information about him:

MeuStruct* objeto = (MeuStruct*)malloc(sizeof(MeuStruct));
objeto.a = 10;
objeto.b = "teste"; // Erro de compilação - o compilador sabe que o tipo de b é float

If you want more "reflectivity" - the ability to inspect an object at runtime and discover its structure - you need at the very least of a further field, containing an abstract representation of this type:

struct Tipo {
    char* nome;
    char** nomesCampos;
    Tipo** tiposCampos;
    ...
}

struct MeuStruct {
    Tipo* minhaClasse;
    int a;
    float b;
    char* c;
}

Object-oriented programming languages must necessarily implement something like this in all the complex data types. Usually, they hide this from the programmer, not to "confuse" what is of the program and what is of the language itself (in the example above, a, b and c are fields defined by the programmer, and minhaClasse is only accessible to the execution environment). So the programmer can only access this special field indirectly, through other methods of the language itself:

var x = 10; // O objeto 10 tem uma referência para [uma representação abstrata de] seu tipo
alert(typeof x); // "number" - o nome de seu tipo

var y = { a:1, b:0.1, c:"teste" }; // idem
alert(typeof y); // "object"

for ( var p in y ) { // O tipo de y possui uma lista com os nomes de todos os seus campos
    alert(y[p]); // Ele também consegue em tempo de execução acessar um campo por nome
}                // (usando a informação do tipo para chegar no endereço de memória correto)

These fields I called "special" are commonly called "interns" - because they are not "out of the box" (e.g.: the buttons of a remote control, which you can press), but "inside" (e.g.: the circuits that make up the same, which you cannot touch). They exist, they’re there in memory, but you can’t touch them.

Heritage and Prototypes

What about inheritance? Languages that use object orientation classical only allow "types to inherit from types": an object cannot inherit directly from another object, so that no other special field is needed besides the minhaClasse. The compiler/Runtime makes sure that objects are created with enough memory to hold all fields of their own type and of its (s) supertype(s), if any. It can do some additional "magic" so that the fields have default values, constructor functions are called in the right order, etc., but ultimately an object of the subtype has no relation to any object of its supertype.

Javascript implements object orientation prototypical - there are still types (e.g., object, number, string, Boolean), but the usual way to "inherit structure and functionality" is by having an object inherit from another object. More specifically, the child object to be consulted for a property he nay possesses (including functions) instead of "giving up" he seeks that property on the parent object. Emphasizing, he does not own a copy of his father’s property, he actually uses the property contained in his father’s memory region. To be able to do that, naturally, he needs an explicit reference to know who is the parent object.

This parent object is called a "prototype". There is no theoretical explanation about the concept (if you are interested, I suggest that section of that Steve Yegge article [in English]) nor will I describe the specificities of the prototype in the Javascript language (the linked question already covers this very well), I just want the following point to be clear:

The prototype is a field intern, so it does not mix with the normal fields defined by the programmer.

You cannot access the prototype of an object directly. A specification Ecmascript refers to him as [[Prototype]], but that doesn’t mean you can do:

objeto["[[Prototype]]"]

and gain access to it (read or write). No, this field is internal to the Javascript engine, and yet some implementations decide to expose it (and not necessarily under the same name - see __proto__) they have no obligation to do so. Just assume that the prototype is there, that each object knows who its prototype is, knows to delegate to it correctly the query to missing properties, and that the only "correct" way to access the prototype programmatically (reflection) is through special functions of the language itself (Object.getPrototypeOf).

Syntax

If you can’t access the prototype directly, what is the prototype? First of all, it’s a "normal" property, you could put it on whichever object and yet it would not be the prototype of this object:

var x = { a:10 };
var y = { b:20, prototype:x };
alert(y.a); // undefined - y não herda de x

The "prototypical" way to create an object you inherit from another in Javascript is via direct attribution (item 2 of answer to the linked question):

var x = Object.create(null); // O [[Prototype]] de x é nulo...
x.a = 10;                    // ...e ele possui uma propriedade "a" valendo 10
var y = Object.create(x);    // O [[Prototype]] de y é x...
y.b = 20;                    // ...e ele possui uma propriedade "b" valendo 20

alert(y.a); // 10
Object.getPrototypeOf(y) == x; // true

If Javascript had been well designed</Rant> this would be the only way. However, for historical reasons (reasons that are also responsible for the name "Javascript" - the language originally called "Livescript", saving us all from having to explain to a lot of people that "Javascript is different from Java", among many other annoyances<agorasim/Rant>) was required by its author that its syntax "resemble that of Java" - including the apparent classical inheritance and the use of constructors and the keyword new:

function ClasseDeX() { // Desnecessário...
    this.a = 10;
}
var x = new ClasseDeX(); // idem...

What about the inheritance? Some kind of ClasseDeY extends ClasseDeX it was necessary, but the semantics Javascript was still prototypical, which required each object to have a prototype. It was not possible for "the type of y" to inherit from "the type of x", it was mandatory that the y inherited from a specific object (and not from the class in general). For reasons I have no idea whether it was decided that this would be done by saying to the builder which object he should use as a prototype of all his objects, through the property prototype:

function ClasseDeY() {
    this.b = 20;
}
ClasseDeY.prototype = x; // Aqui estou instruindo o construtor a usar x como protótipo de
                         // todo objeto criado via "new ClasseDeY()"

var y = new ClasseDeY(); // O campo especial [[Prototype]] de y agora contém referência pra x

alert(y.a); // 10
Object.getPrototypeOf(y) == x; // true

Confused? Don’t tell me...

Finally, a note on the literals for object: if you do not need inheritance, there is no reason to create an object that is a prototype of another object, the use of literals can save a lot of keyboard:

// Isso:
var obj = Object.create(null);
obj.a = 1;
obj.b = 0.1;
obj.c = "teste";

// É equivalente a isso:
var obj = { a:1, b:0.1, c:"teste" };

Now, when you want or need an object to have a prototype (for example, if your object has dozens of methods, and you don’t want to waste memory by having each instance have an explicit reference to all these methods) a literal is not an option (unfortunately...). Another means is necessary. The most "clean" would be via Object.create, but he has a small but...

var x = { a:10 };
//var y = Object.create(x, { b:20 });         // Errado
var y = Object.create(x, { b:{ value:20 } }); // Certo

Unfortunately the second argument to Object.create (the first is the object to be used as a prototype) it is not a simple object: it is a "property descriptor". Associated with each name must be not a simple value, but a series of data describing what that property should have, for example:

  • Its initial value (value);
  • Whether it is "configurable" or not (whether its characteristics can change at runtime);
  • Whether it is enumerable or not (if it appears in a for ... in);
  • Whether it is read-only or not;
  • getter and Setter for her (the precise semantics of the same, I have no knowledge).

That makes, in my opinion, far less convenient to use in this way, so that many prefer the "pseudo-classic" method even despite the confusing syntax (many "decorate" the way of doing and do not think much on the subject - myself for example, for years...). In the prototype, however, one can use a literal (when one does not want to inherit anything) or an instance of another object:

function MinhaClasse(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
}
MinhaClasse.prototype = {
    funcao1:function() { ... },
    funcao2:function() { ... },
    ...
    funcaoN:function() { ... }
};

var obj = new MinhaClasse(1, 0.1, "teste"); // Só gasta memória com seus próprios campos;
                                            // As funções são herdadas do protótipo

Over time people have been developing various standards to make programming easier, which is not worth enumerating here. The important thing is to know that the above syntax is essentially equivalent to using a normal, nonconstructor function:

function MinhaClasse(a, b, c) {
    var self = Object.create(MinhaClasse.prototype);
    self.a = a;
    self.b = b;
    self.c = c;
    return self;
}
MinhaClasse.prototype = {
    funcao1:function() { ... },
    funcao2:function() { ... },
    ...
    funcaoN:function() { ... }
};

var obj = MinhaClasse(1, 0.1, "teste"); // Repare a ausência do "new"

This way the effect is the same, and without the "magic" of prototype which makes it seem like it is a special field, when in fact it is a normal field like any other (note that in the example above I could have used any other variable to store the MinhaClasse.prototype, provided that the object itself was unique).

What’s "more right"? Can’t answer that, right is the one you and his team understand well enough not to make any mistake, and communicate clearly to each other what that code does. Regarding performance, I noticed that in V8 (Chrome, Node.js) use or not constructors and the new makes a glaring difference, in others browsers, not so much. But unless you’re facing problems real performance, let micro-optimizations go and focus on the clarity of your code.

  • @mgibsonbr summarizing: prototype is an object within another?

  • 1

    @ropbla9 I wouldn’t call "inside". In x = { y:{ a:10 } } the object y is within x, he’s a component of x. If you want the value of a you have to do x.y.a. The prototype is a base from which you specify the other. Imagine a football. It’s round, you can kick, etc. A football is like a football, only it’s oval. This "only oval" is specific to it. But you can still kick (it still looks like your prototype). An object looks like its prototype in everything that is not specific to it.

  • 1

    For a more complete example, imagine a chair: it has a seat (flat, flat, horizontal) supported by 4 legs, at a height of about 0.5m, and on one side it has a backrest. Suppose this is represented by an object. If I wanted to describe a table, I could describe it completely, as I did with the chair. But I could also say that a table "is like a chair", only it has no backrest and its height is about 0.8m. She continues to have a flat, flat "seat", flat, horizontal and supported by 4 legs, but I didn’t have to say this explicitly. She inherited this.

  • @ropbla9 I added two sections at the beginning of the answer with a little more information. I hope it helps (and not that confuses you even more rsrs).

  • Excellent explanation! After reading several texts and articles, I finally managed to understand what is and what makes the prototype in javascript! :)

4

Prototypes can be understood easily by bringing real-life things into programming.

Take an example, I have the function Pessoa in Javascript, which can be understood in this context as an object:

function Pessoa() { /* perceba que ela não faz nada */ }
var Diego = new Pessoa();
var Maria = new Pessoa();
var Joao = new Pessoa();

Where I created Diego, Maria and João to illustrate this example, they are instantiated objects of the function Pessoa, but do nothing. Let’s assume that I want to create a prototype and allow them to emit a sound:

function Pessoa() {}
 Pessoa.prototype.Falar = function () {
    /* Emitir som */
 }

Now every time I run Maria.Falar(); Maria will be making a sound, just like everyone else. I may also be making people perform other functions, such as running, walking, sleeping, calculating, etc.

 Pessoa.prototype.Correr = function() {
   /* Fazer o objeto se movimentar muito rápido na tela */
 }

 Pessoa.prototype.Andar = function() {
   /* Fazer o objeto se movimentar normalmente na tela */
 }

Pessoa.prototype.Calcular = function() {
   /* Fazer um cálculo */
}

Pessoa.prototype.Dormir = function() {
   /* Aparecer um Zzz... no objeto */
}

And to execute the methods and allow these people to talk, run, walk and sleep:

Maria.Falar();
Maria.Dormir();
Maria.Correr();
Maria.Andar();
Diego.Falar();
Diego.Dormir();
Diego.Andar();
Diego.Correr();
  • turns out I don’t know this way to create objects with functions. I only know how to create with constructor and literal. That’s what confuses me too =/

  • @ropbla9 I don’t know Javascript, but I did find an interesting article on the tableless http://tableless.com.br/dominando-uso-de-prototype-em-javascript/ I don’t know if it helps you.

  • then but this article is explained with functions. I will try to understand this syntax first of all. The logic of the prototype I understand more or less.

  • There are POO tables that show the functioning of prototypes, this helps a lot to understand the syntax.

  • 1

    @ropbla9 In Javascript, any function acts as a constructor if called with new.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.