JavaScript Development Insights–Escopo e Criação de Classes

JavaScript

Antes de continuar a série “HTML5 Game Development” vou falar de alguns temas chave quando estamos escrevendo código para games e APPs com JavaScript. Antes de construir uma casa temos de fazer o fundamento, pelo menos se queremos algo com qualidade e durabilidade. Sendo assim é fundamental dominar alguns pontos da linguagem. Smiley piscando

capa

O código acima “parece” totalmente despretensioso e simples porém as duas linhas acima NÃO tem o mesmo efeito. O Script A e o Script B escondem uma diferença fundamental para o tema em questão. O simples fato de utilizar ou não a palavra chave var, pode alterar drasticamente o sentido e função do código.

JavaScript é uma linguagem orientada a objetos muito flexível quando se trata de sintaxe, mesmos assim é importante lembrar que não há classes em JavaScript. Com um conhecimento melhor da linguagem podemos simular classes, mas, em geral, JavaScript é class-less language. Tudo em JavaScript é um objeto. E quando se trata de herança, os objetos herdam de objetos e não classes a partir de classes.

Para facilitar o estudo, você pode baixar todos os scripts utilizados neste post clicando aqui!!!

 

Revisando

Como falei anteriormente, podemos dizer que existem duas formas para se criar um objeto: Usando object literal ou constructor function.

codigo0

Embora os métodos dos objetos sejam iguais, eles são diferentes (???). Um object literal cria um objeto que pode ser imediatamente utilizado sem primeiro ser "instanciado" com a palavra-chave new. Neste ponto um object literal também é conhecido como static. O porém da história e que objetos statics não podem implementar herança e encapsulamento no JavaScript.

Já utilizado a function constructor podemos implementar orientação a objetos, simulando o comportamento de uma classe.

UPDATE: Para quem já leu minha referência ao object  literal como singleton, estou atualizando para static. Correção sugerida pelo Júlio Ködel, já que por definição em Singleton podemos implementar herança.

 

Escopo

O primeiro passo é entender sobre o Escopo. Em JavaScript escopo é o contexto em que uma entidade, seja ela variável, função ou objeto é acessível ou visível a outras entidades.

Temos alguns tipos de escopo porém vou abordar de uma maneira simplificada apenas tratando tudo como Escopo Global ou Escopo Local.

Escopo Global

O Escopo global é o mais alto nível de contexto e é definido pelo Window Object. Tudo se inicia no escopo global e qualquer entidade definida no escopo global pode ser acessada em qualquer parte do código.

Escopo Local

O escopo local é toda entidade definida fora do contexto global (Sério???). Uma entidade de escopo corrente ou local, só é acessível dentro de seu próprio escopo e não pode ser acessado em  outro escopo, incluindo o escopo global. Vamos a um exemplo:

codigo01

No código acima criamos uma variável e uma função de escopo global. Já em nossa função estamos criando uma variável de escopo local. Ao executar nossa função de escopo global conseguimos exibir os resultado corretamente por que é possível acessar o valor da variável global e local. Agora se tentarmos exibir os valores das duas variáveis no contexto global veremos que o resultado para a variável local será ReferenceError: local is not defined.

O escopo atual pode ser acessado usando o objeto this. Este objeto aponta para o escopo que está sendo utilizado no momento. Se estivermos no escopo global o objeto this vai apontar para o Window object. Já se estivermos em um escopo local, this vai apontar para o Current Object. Vamos ao exemplo abaixo:

codigo02

O escopo dentro de um objeto ou função é algo um pouco mais complexo, pois depende da definição do mesmo. Vamos voltar a introdução deste post…

Para declarar a entidade você pode usar as palavras chave var ou this. Quando criamos uma entidade usando o var, ela se torna uma entidade de escopo local. Já entidades declaradas utilizando o this se tornam uma propriedade do objeto no escopo atual. Isso significa que a entidade pode ser acessada fora do escopo corrente. Vamos ao exemplo.

codigo03

Neste exemplo temos uma função Triangulo que exibe a altura do mesmo baseado na sua área. Note que _altura é declarado utilizando var. Para as demais propriedades estamos utilizando this. Neste caso ao acessar diretamente nossas entidades, vemos que a declaração this expos as mesmas fora do escopo corrente. Já a declaração de _altura utilizando var só é acessível dentro do escopo local.

É fundamental entender o funcionamento do escopo para a correta implementação da orientação a objeto e criação de entidades públicas e privadas.

 

Criando Classes!?!

Como Já falei anteriormente JavaScript não é uma linguagem baseada em classes e sim orientada a protótipos. Não temos classes em JavaScript porém com o conhecimento que já temos sobre o funcionamento do escopo de uma entidade e criação dos objetos, é possível discutir em como implementar classes no JavaScript.

Já vimos que o dependendo da declaração do objeto suas entidades podem estar no escopo global (públicas e acessíveis fora do objeto), ou local (privadas e acessíveis apenas no objeto).  Vejamos o exemplo abaixo:

codigo04

Estamos definindo um polígono qualquer e queremos saber sua área e perímetro. Como implementamos nossa função exibirArea apenas no escopo local do objeto, ao tentar chamar no escopo global obtemos um erro. Já a função exibirPerimetro é acessível no escopo global como uma simples propriedade do objeto Poligono.

Funções com construtor seguem o mesmo princípio, só que neste caso os parâmetros são de escopo local.

codigo05

Vale fazer uma parênteses neste ponto para lembrar que em JavaScript quando não informamos o valor de um parâmetro para um função com construtor, ele vai por padrão atribuir um “value of undefined”. Uma forma de tratar isso é definir em sua função um retorno padrão caso o parâmetro não seja informado.

codigo06

Ao contrário das linguagens baseadas em classes, em JavaScript não temos sobrecarga (overloading) de funções. Não adianta alterar a quantidade de parâmetros. Se você criar duas entidades com o mesmo nome no mesmo escopo a última entidade vai sobrescrever a primeira. Vamos tirar a prova:

codigo07

Por outro lado é possível ter uma função com o número variável de argumentos.  Como exemplo de funções com argumentos múltiplos podemos citar Math.max() e Math.min(). Como pode isso??? Em JavaScript podemos utilizar o objeto arguments que é basicamente uma matriz de todos os argumentos passados para a função.

Notem que eu o objeto arguments é "basicamente" uma matriz, porém existem algumas considerações necessárias que pretendo decorrer no próximo post. Independente disto este é um poderoso recurso para resolver nosso problema.

codigo08

 

Adicionando novas propriedades

Neste ponto vamos dizer que já sabemos como criar nossa função com construtor derivando de um objeto. Agora imagina que em dado ponto é necessário adicionar uma nova "propriedade" ao objeto. Em JavaScript temos a facilidade de realizar alterações nos objetos em tempo de execução. Sendo assim novas entidades adicionadas serão entendidas como propriedades do objeto, sendo acessadas como entidades declaradas no objeto utilizando a palavra-chave this. Vejamos o exemplo abaixo:

codigo09

Agora existe um porém. Tenha em mente que ao criar novas entidades utilizando este mecanismo, elas não serão refletidas nos outros objetos que derivam da mesma função. Vamos a prova:

codigo10

A melhor maneira para tratar isso é com a utilização do Prototype como escrevi neste post!!!

 

Conclusão

Como vimos é possível imitar o comportamento das classes usando funções com construtor. Sendo assim podemos usar os princípios da Orientação como encapsulamento para criar entidades públicas e privadas.

 

Bons estudos e até a próxima pessoal  ;)


Author's profile picture

Vitor is a computer scientist who is passionate about creating software that will positively change the world we live in.

MVP Azure - Cloud Architect - Data science enthusiast


7 minutes to read