mozilla
Os seus resultados da pesquisa

    Herança e cadeia de protótipos (prototype chain)

    Esta tradução está incompleta. Ajude atraduzir este artigo.

    JavaScript é um pouco confuso para desenvolvedores com experiência em linguagens baseadas em classes (como Java ou C++), porque é dinâmico e não dispõe de uma implementação de class (através da palavra-chave reservada class e não pode ser usada com um nome de variável).

    Quando se trata de herança, JavaScript tem somente um construtor: objetos. Cada objeto tem um link interno para um outro objeto chamado prototype. Esse objeto prototype também tem um atributo prototype, e assim por diante até que null seja encontrado como prototype (como em uma implementação de fila em C, por exemplo). null, por definição, não tem prototype, e age como um link final em um encadeamento de protótipos (prototype chain).

    Isto muitas vezes é considerado como um dos pontos fracos do JavaScript, mas o modelo de herança prototipal é de fato mais potente do que o modelo clássico. É, por exemplo, relativamente trivial para construir um "modelo clássico" (como na implementaçao de class), enquanto o contrário é uma tarefa muito mais difícil.

    Herança com o encadeamento de protótipos

    Propriedades de heranças

    Objetos em JavaScript são "sacos" dinâmicos de propriedades (a que se refere as proprias propriedades) e cada um tem um link para um objeto prototype. Eis o que acontece quando se tenta acessar uma propriedade:

    // Let's assume we have an object o with its prototype chain looking like:
    // {a:1, b:2} ---> {b:3, c:4} ---> null
    // 'a' and 'b' are o own properties.
    
    // In this example, someObject.[[Prototype]] will designate the prototype of someObject.
    // This is a pure notation (based on the one used in the ECMAScript standard) and cannot be used in scripts.
    // The equivalent property to use in scripts is called "__proto__" as in someObject.__proto__
    
    console.log(o.a); // 1
    // Is there an 'a' own property on o? Yes and its value is 1
    
    console.log(o.b); // 2
    // Is there a 'b' own property on o? Yes and its value is 2
    // The prototype also has a 'b' property, but it's not visited. This is called "property shadowing"
    
    console.log(o.c); // 4
    // Is there a 'c' own property on o? No, check its prototype
    // Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4
    
    console.log(o.d); // undefined
    // Is there a 'd' own property on o? No, check its prototype
    // Is there a 'd' own property on o.[[Prototype]]? No, check its prototype
    // o.[[Prototype]].[[Prototype]] is null, stop searching, no property found, return undefined
    

    Setting a property to an object creates an own property. The only exception to the getting and setting behavior rules is when there is an inherited property with a getter or a setter.

    Herença de "metodos"

    JavaScript nao tem "metodos" como os que conhecemos em linguagens baseadas em classes. Em JavaScript, qualquer funçao pode ser adicionada em um objeto em forma de propriedade. Uma herança de funçoes age como a herença de quaisquers outras propriedades que não sejam funções, e podemos inclusive realizar sobre-escrita de função!

    Quando uma herança de função é executada, o valor de this aponta para o objeto que herdou as propriedades, não para o objeto prototype onde as propriedades foram escritas originalmente.

    var o = {
      a: 2,
      m: function(b){
        return this.a + 1;
      }
    };
    
    console.log(o.m()); // 3
    // When calling o.m in this case, 'this' refers to o
    
    var p = Object.create(o);
    // p is an object that inherits from o
    
    p.a = 12; // creates an own property 'a' on p
    console.log(p.m()); // 13
    // when p.m is called, 'this' refers to p.
    // So when p inherits the function m of o, 'this.a' means p.a, the own property 'a' of p
    

    Maneiras de criar objetos e resultados dos protótipos encadeados

    Objetos criados com sintaxe de construtores

    var o = {a: 1};
    
    // The newly created object o has Object.prototype as its [[Prototype]]
    // o has no own property named 'hasOwnProperty'
    // hasOwnProperty is an own property of Object.prototype. So o inherits hasOwnProperty from Object.prototype
    // Object.prototype has null as its prototype.
    // o ---> Object.prototype ---> null
    
    var a = ["yo", "whadup", "?"];
    
    // Arrays inherit from Array.prototype (which has methods like indexOf, forEach, etc.)
    // The prototype chain looks like:
    // a ---> Array.prototype ---> Object.prototype ---> null
    
    function f(){
      return 2;
    }
    
    // Functions inherit from Function.prototype (which has methods like call, bind, etc.)
    // f ---> Function.prototype ---> Object.prototype ---> null
    

    Com um construtor

    Um "construtor" em JavaScript é "somente" uma função que passa a ser chamada com o operador new.

    function Graph() {
      this.vertexes = [];
      this.edges = [];
    }
    
    Graph.prototype = {
      addVertex: function(v){
        this.vertexes.push(v);
      }
    };
    
    var g = new Graph();
    // g is an object with own properties 'vertexes' and 'edges'.
    // g.[[Prototype]] is the value of Graph.prototype when new Graph() is executed.
    

    Com Object.create

    ECMAScript 5 introduziu o novo método: Object.create. Invocando este método podemos criar novos objetos. O prototype destes novos objetos é o primeiro argumento do método:

    var a = {a: 1}; 
    // a ---> Object.prototype ---> null
    
    var b = Object.create(a);
    // b ---> a ---> Object.prototype ---> null
    console.log(b.a); // 1 (inherited)
    
    var c = Object.create(b);
    // c ---> b ---> a ---> Object.prototype ---> null
    
    var d = Object.create(null);
    // d ---> null
    console.log(d.hasOwnProperty); // undefined, because d doesn't inherit from Object.prototype
    

    Performace

    O tempo de pesquisa para as propriedades que estão no alto da cadeia de protótipos pode ter um impacto negativo no desempenho, e isso pode ser significativo no código em que o desempenho é crítico. Além disso, tentando acessar propriedades inexistentes vai sempre atravessar a cadeia cheia do protótipo (full prototype chain).

    Porém, quando estamos interagindo com as propriedades de um objeto, toda propriedade que está na cadeia do prototype (prototype chain) vai ser enumerada.

    Para verificar se um objeto tem uma propriedade definida em si mesmo e não em algum lugar na sua cadeia de protótipo, é necessário usar o método hasOwnProperty que todos os objetos herdam do Object.prototype.

    hasOwnProperty is the only thing in JavaScript which deals with properties and does not traverse the prototype chain.

     
    Observação: Não é suficiente apenas verificar se o valor da propriedade é undefined para saber se ela existe. A propriedade pode muito bem existir e não ter sido inicializada, sendo assim o seu valor undefined.

    Má Pratica: Estender protótipos nativos

    Um erro frequentemente cometido por programadores é estender um Object.prototype.

    Esta técnica é chamada de "monkey patching" e quebra o encapsulamento. Não existe uma boa razão para desorganizar tipos nativos do JavaScript para dicionar uma nova funcionalidade ao mesmo. 

    O único bom motivo para estender um protótipo nativo do JavaScript é para dar suporte a novas "features" do JavaScript; por exemplo: Array.forEach, etc.

    Conclusão

    É essencial entender bem  "prototypal inheritance" antes de escrever códigos complexos. Tome cuidado com o tamanho da sua cadeia de protótipos, quebre a cadeia caso necessário para evitar problemas de performace. Nunca estenda protótipos nativos a menos que seja para conseguir compatibilidade com novas "features" do JavaScript.

     

    Etiquetas do documento e colaboradores

    Última atualização por: Felipe_Baravieira,