Ir para o conteúdo

Objetos e atributos publicos, privados, static e non-static

O Javascript é uma linguagem direcionada a objetos, mas não funciona com classes e, por isso, quando se programa objetos Javascript não se está a programar como se faz em Java ou em PHP.

Requisitos

Para este tutorial irei partir do princípio de que (requisitos):

  • Sabem o que é e como funcionar com funções com um nível intermédio em Javascript
  • Já sabem criar um objeto em Javascript com métodos e atributos
  • Sabem aceder a uma variável num objeto (um atributo ou um método)

Aqui irei falar de uma maneira simplificada sobre como obter variáveis de modo a poder haver variáveis públicas e privadas (não existem variáveis protegidas). Também indicarei como criar o que em Java são variáveis de classe.

Privadas? Porquê?

Por que raio é que eu quererei ter variáveis privadas? Existem duas razões principais para isso:

  • Quando código é feito, é garantido que o que é público quando este é criado irá continuar público e a funcionar da mesma maneira. Ao tornar algumas coisas privadas, poderás alterar o que te apetecer e melhorar como te apetecer e garantir que tudo irá continuar a funcionar como queres e sem erros tanto para ti como para quem usar o teu código, isto no que é privado (claro).
  • Podemos não querer que partes do nosso objeto sejam acessíveis a partir do exterior, pois isto poderia tornar o nosso objeto instável e não funcionar como queremos. Assim tem-se a certeza de que não irão aparecer reclamações de não funcionar bem.

Tentativa 1 de ter o objetivo feito

Código

Suponhamos que temos o seguinte objeto javascript:

function Pessoa (pNome, uNome, idade, corPele){

    this.pNome = pNome;
    this.uNome = uNome;
    this.idade = idade;
    this.corPele = corPele;
    this.fome = false;
    setTimeout(
                function (){
                    this.fome = true;
                }
                , 1000
                );
    this.sono = false;
    setTimeout(
                function (){
                    this.sono = true;
                }
                , 5000
                );

    this.getcor = 
        function (){
            return this.cor;
        }

    this.getNomeCompleto = 
        function (){
            return this.pNome + " "+ this.uNome;
        }

    this.getPrimeiroNome = 
        function (){
            return this.uNome;
        }

    this.getUltimoNome = 
        function (){
            return this.uNome;
        }

    this.getIdade = 
        function (){
            return this.idade;
        }

    this.getCorPele = 
        function (){
            return this.corPele;
        }

    this.come = 
        function (){
            this.fome = false;
            setTimeout(
                function (){
                    this.fome = true;
                }
                , 1000
                );
        }

    this.dorme = 
        function (){
            this.sono = false;
            setTimeout(
                function (){
                    this.sono = true;
                }
                , 5000
                );
        }

}

Problema

"Isto é giro, serve para representar uma pessoa no meu modelo. Ele come, dorme... Oh não!!! Quem usar o objeto consegue fazer batota! Todos os atributos são públicos! Os utilizadores do meu objeto conseguem fazer batota em relação ao dormir alterando diretamente no parâmetro. E agora?" Agora... Há que tornar coisas só de leitura... só de leitura. Para isso basta usar o que o Javascript tem para nos oferecer. O tão conhecido var.

Solução possível

Tudo o que for declarado usando o var fica inacessível para o exterior do objeto (parâmetros privados). Usando essa informação iremos alterar o Pessoa para cumprir isso. Iremos chamar a este objeto Pessoa2:

Tentativa 2 de ter o objetivo feito

Código

function Pessoa2 (pNome, uNome, idade, corPele){

    this.pNome = pNome;
    this.uNome = uNome;
    this.idade = idade;
    this.corPele = corPele;
    var fome = false;
    setTimeout(
                function (){
                    fome = true;
                }
                , 1000
                );
    var sono = false;
    setTimeout(
                function (){
                    sono = true;
                }
                , 5000
                );

    this.getcor = 
        function (){
            return this.cor;
        }

    this.getNomeCompleto = 
        function (){
            return this.pNome + " "+ this.uNome;
        }

    this.getPrimeiroNome = 
        function (){
            return this.uNome;
        }

    this.getUltimoNome = 
        function (){
            return this.uNome;
        }

    this.getIdade = 
        function (){
            return this.idade;
        }

    this.getCorPele = 
        function (){
            return this.corPele;
        }

    this.come = 
        function (){
            fome = false;
            setTimeout(
                function (){
                    fome = true;
                }
                , 1000
                );
        }

    this.dorme = 
        function (){
            sono = false;
            setTimeout(
                function (){
                    sono = true;
                }
                , 5000
                );
        }

}

Problema

Assim sim. O que deve ser privado é privado, o que é público, é público. Agora outro problema. Quero que cada pessoa tenha um id único. Este id irá ser um número que incrementa a cada vez que uma pessoa é criada. E mais! Eu nunca poderei confiar que o utilizador do meu objeto mantenha o número atualizado e correto. Como é que irei resolver isto?

Solução possível

Para isto não há nada melhor que usar uma característica do Javascript que não existe em Java. Em Javascript uma função é um objeto (e o objeto pode não ser uma função). Em Javascript existem variáveis do tipo function e Object. Isto permite fazer coisas que o Java pode só imaginar. Neste caso é para fazer algo que o Java tem nativamente :D.

De qualquer modo, para fazer isto precisamos do paradigma que se baseia no conceito de uma função que devolve uma função. Todas as variáveis declaradas na função exterior estão acessíveis na função interior (a não ser que sejam escondidas).

Tentativa 3 de ter o objetivo feito

Código

var Pessoa3 = 
    function (){
        var id = 0;

        return  function Pessoa3 (pNome, uNome, idade, corPele){

            // Grava o id deste objeto
            var objId = id;
            // Prepara o id para o objeto seguinte
            id++;

            this.pNome = pNome;
            this.uNome = uNome;
            this.idade = idade;
            this.corPele = corPele;

            var fome = false;
            setTimeout(
                        function (){
                            fome = true;
                        }
                        , 1000
                        );
            var sono = false;
            setTimeout(
                        function (){
                            sono = true;
                        }
                        , 5000
                        );

            this.getcor = 
                function (){
                    return this.cor;
                }

            this.getNomeCompleto = 
                function (){
                    return this.pNome + " "+ this.uNome;
                }

            this.getPrimeiroNome = 
                function (){
                    return this.uNome;
                }

            this.getUltimoNome = 
                function (){
                    return this.uNome;
                }

            this.getIdade = 
                function (){
                    return this.idade;
                }

            this.getCorPele = 
                function (){
                    return this.corPele;
                }
            this.getId = 
                function (){
                    return objId;
                }

            this.come = 
                function (){
                    fome = false;
                    setTimeout(
                        function (){
                            fome = true;
                        }
                        , 1000
                        );
                }

            this.dorme = 
                function (){
                    sono = false;
                    setTimeout(
                        function (){
                            sono = true;
                        }
                        , 5000
                        );
                }

        }
    }();

Explicação

Há muitos conceitos por detrás disto e pode-se demorar até bastante tempo para compreender tudo o que ocorre aqui. Então o que é que ocorre aqui? Em primeiro lugar, tenho que gravar tudo numa variável. Chamei-lhe Pessoa3. Se quiser criar um objeto Pessoa3 basta escrever:

new Pessoa3("Joao", "coiso", 15, "#FFFFFF");

Isto porque a variável chama-se:

var Pessoa3

independentemente do nome que der ao objeto (que, neste caso, tem o mesmo nome).

Já devem ter reparado que no final da função exterior têm um:

}();

A razão para isto é muito simples. Como eu quero que ninguém obtenha acesso à função exterior então executo-a no momento em que a crio. Assim, nenhum código terá acesso à função exterior, ou seja, o id nunca poderá ser reiniciado, o que significa anonimato completo do utilizador perante a função exterior, que significa que o id único ficará acessível só a partir do objeto, e que o id nunca irá ser modificado por código exterior. Ao executar a função exterior ele retorna uma referência para o construtor do objeto que é o que irá ficar gravado na variável Pessoa3.

Tentativa 4 de ter o objetivo feito

Intro

Para finalizar este tutorial vamos supor que eu quero manter a contagem do número de pessoas que têm fome. Vejamos o que é que eu preciso:

  • O número atual da contagem tem que ser algo que o público tenha acesso para saber.
  • O público não pode alterar o número.
  • Todas as classes têm que ter acesso à mesma contagem (ou a contagem só teria os números 1 e 0).

Conclusão: Uma variável de classe com um método de classe (do ponto de vista de uma linguagem direcionada a objetos com classes) para obter o valor. Tendo isso em conta irei mostrar como fazer isso em javascript que não tem classes.

Código

Vejamos o código: nota: para este código, tudo o que é importante do ponto de vista deste tutorial está comentado. O resto foi deixado por comentar

var Pessoa4 = 
    function (){
        // Aqui aparecem as variáveis, as constantes e os métodos "de classe"
        var id = 0;
        var nPessoasComFome = 0;

        // É uma função geral. Não é preciso refazê-la para cada objeto.
        // + rápido e ocupa menos espaço para além de estar num sítio mais lógico
        var getNumeroDePessoasComFome = function (){
            return nPessoasComFome;
        }

        return function Pessoa4 (pNome, uNome, idade, corPele){

            // Grava o id deste objeto
            // Uma vez gravado não é suposto alterar, por isso, é melhor gravar como uma constante
            const objId = id;

            // Prepara o id para o objeto seguinte
            id++;

            // Associa tudo o que tem a ver com os atributos aos atributos
            // Estes atributos são publicos
            this.pNome = pNome;
            this.uNome = uNome;
            this.idade = idade;
            this.corPele = corPele;

            // Defino as variáveis privadas e como elas funcionam
            var fome = false;
            setTimeout(
                        function (){
                            fome = true;
                            nPessoasComFome++;
                        }
                        , 1000
                        );
            var sono = false;
            setTimeout(
                        function (){
                            sono = true;
                        }
                        , 5000
                        );

            // Defino os métodos públicos do objeto
            this.getTemFome = 
                function (){
                    return fome;
                }

            this.getTemSono = 
                function (){
                    return sono;
                }

            this.getcor = 
                function (){
                    return this.cor;
                }

            this.getNomeCompleto = 
                function (){
                    return this.pNome + " " + this.uNome;
                }

            this.getPrimeiroNome = 
                function (){
                    return this.uNome;
                }

            this.getUltimoNome = 
                function (){
                    return this.uNome;
                }

            this.getIdade = 
                function (){
                    return this.idade;
                }

            this.getCorPele = 
                function (){
                    return this.corPele;
                }

            // Pomos público o saber o id do objeto
            this.getId = 
                function (){
                    return objId;
                }

            // Pomos disponível a função para obter o número que queremos por disponível
            // neste caso, o nº de pessoas com fome
            this.getNumeroDePessoasComFome = getNumeroDePessoasComFome;

            this.come = 
                function (){
                    fome = false;
                    // Este já comeu, menos um com fome
                    nPessoasComFome--;

                    setTimeout(
                        function (){
                            fome = true;
                            // Ficou com fome, regista!
                            nPessoasComFome++;
                        }
                        , 1000
                        );
                }

            this.dorme = 
                function (){
                    sono = false;
                    setTimeout(
                        function (){
                            sono = true;
                        }
                        , 5000
                        );
                }

        }
    }();