Solucionado (ver solução)
Solucionado
(ver solução)
9
respostas

Diferença entre "let $ =" e "this._$ = ", e avaliação de método de instanciamento de uma negociação

Olá pessoal!

Abaixo seguem a classe NegociacaoController e as dúvidas, respectivamente:

class NegociacaoController {

  constructor() {
    // let $ = document.querySelector.bind(document);

    // this._inputData = $("#data");
    // this._inputQuantidade = $("#quantidade");
    // this._inputValor = $("#valor");

    // this._form = $(".form");


    this._$ = document.querySelector.bind(document);

    this._inputData = this._$("#data");
    this._inputQuantidade = this._$("#quantidade");
    this._inputValor = this._$("#valor");

    this._form = this._$(".form");
  }

  adiciona(event) { 
    event.preventDefault();

    let negociacao = this.criaNegociacao(this._inputData, 
      this._inputQuantidade, this._inputValor);

    this.reiniciaFormulario();

    console.log(negociacao.data, negociacao.quantidade, negociacao.valor);
  }

  criaNegociacao(inputData, inputQuantidade, inputValor) {
    // transformação da data do input em array[3](mm/dd/yyyy), 
    // em formato compatível com a classe Date
    let data = new Date(inputData.value.split('-'));

    return new Negociacao(data, inputQuantidade.value, inputValor.value);  
  }

  reiniciaFormulario() {
    this._form.reset();
    this._inputData.focus();
  }  
}

Dúvidas:

  • se o método que cria/retorna uma instância de negociação (criaNegociacao()) estaria correto (e mesmo estando correto/funcionando, se seria a forma mais apropriada);

  • o código comentado acima no construtor é o que foi passado na aula. Já olhei no fórum deste exercício e não encontrei ninguém que tenha feito desta forma: substituí a declaração "let $ = ..." por "this._$ =..." (inclusive para os demais desenvolvedores entenderem, conforme a convenção com _ (underline), que esta var/propriedade também é privada).

    • qual das duas formas de declarar é a mais apropriada? Por que?

Desde já obrigado!

At.te.

9 respostas

Boa noite, Elías! Como vai?

Sobre o ponto 1, me parece que está tudo correto sim!

Já em relação ao ponto 2, veja que o $ não é utilizado em nenhum outro ponto da sua classe a não ser no construtor. Portanto, o correto seria fazer let $ = document.querySelector.bind(document) deixando esse cara restrito ao escopo do construtor.

Pegou a ideia? Qualquer coisa é só falar!

Grande abraço e bons estudos, meu aluno!

Olá Gabriel, tudo bem e você?

Obrigado pelo retorno.

Sim, peguei a ideia: o this faz com o que o $ fique visível em toda a classe, por isso que o correto é considerar o let.

Porém o "let $ = " não deveria ser modificado para "let _$ = ", para explicitamente (e conforme a convenção) mostrar que é uma "propriedade" privada da classe? Sem o _ (underline), o desenvolvedor pode alterar o $ na instancia, ex: instanciaNegociacaoControler.$ = "modificado" (claro, com o _ também é possível, mas pelo menos está explícito pela convenção).

Além disso, as demais propriedades (_inputData, etc) também podem ser modificadas em uma instancia, ou seja, não deveria ser adicionado "Object.freeze(this)" no construtor da classe NegociacaoController?

At.te

Elías, antes de responder as suas questões, vamos dar uma olhada no código que vc comentou (no código a seguir eu descomentei as linhas que vc comentou inicialmente):

class NegociacaoController {

  constructor() {
    let $ = document.querySelector.bind(document);

    this._inputData = $("#data");
    this._inputQuantidade = $("#quantidade");
    this._inputValor = $("#valor");

    this._form = $(".form");
  }

  // restante do código omitido.
}

Repare que o $ é apenas uma variável que existe dentro do construtor e não uma propriedade privada da classe, portanto ela só poderia ser alterada dentro do construtor. Dessa forma, ela não recebe o _ (underline) antes de seu nome.

Em relação ao Object.freeze(), ele só se faz necessário quando há um requisito de imutabilidade de nossos dados. Nesse caso não há essa necessidade pois os atributos da classe não são expostos para fora da mesma sendo todos eles privados e utilizados apenas de forma interna à própria classe, e, desse modo, não é preciso utilizar o Object.freeze().

Qualquer coisa é só falar, meu aluno!

// Negociacao.js
class Negociacao {

  constructor(data, quantidade, valor) { 
    this._data = new Date(data.getTime());
    this._quantidade = quantidade;
    this._valor = valor;

    Object.freeze(this);
  }

// restante do código

Olá Gabriel, novamente obrigado pelo retorno.

Desculpa a insistência, mas não gostaria de continuar o curso sem antes entender claramente este tópico.

1 - eu tinha entendido que o _ (underline) devesse ser usado sempre que uma variável/propriedade pudesse ser alterada de alguma forma de fora da classe, ou seja, sem utilizar os respectivos métodos da classe próprios para isso (os métodos "set"), que no meu entendimento é o caso da classe NegociacaoController (a variável $ ou propriedades podem ser alteradas pela instância), ex.:

// arquivo html
// restante do código 
<script>
  let negociacaoController = new NegociacaoController();

  // um desenvolvedor pode fazer isso, ainda mais no $, por não ter 
  // o _ (por mais estranho que pareça, uma desatenção/descuido
  // pode acarretar isso - afinal, se pode ser alterado tudo é possível)
  negociacaoController.$ = "modificado" 
  negociacaoController._inputData = "alterado";  
</script>

2 - Considerando apenas a classe NegociacaoController entendo a tua explicação, porém comparando com a classe Negociacao volta a embaralhar: você comentou que na classe NegociacaoController não há necessidade do "freeze", porque as propriedades não são expostas para fora da mesma, mas o exemplo acima não é um exemplo de possibilidade de exposição para fora da classe? Se isso não é "exposição", não entendi qual é o escopo, o que define quando uma variável/propriedade de uma classe está exposta ou não.

Complementando, se o exemplo acima não é exposição, também não entendo porque as propriedades _quantidade e _valor da classe Negociacao teriam que ter o freeze (a propriedade data entendo, por ser um objeto Date() e pode ser associado por referência à uma outra variável externa), uma vez que (pelo menos até agora) eles também parecem não estar expostos para fora da classe Negociacao...

At.te

Opa, Elías! Vamos lá! Vou respondendo suas questões aos poucos!

1 - eu tinha entendido que o _ (underline) devesse ser usado sempre que uma variável/propriedade pudesse ser alterada de alguma forma de fora da classe, ou seja, sem utilizar os respectivos métodos da classe próprios para isso (os métodos "set"), que no meu entendimento é o caso da classe NegociacaoController (a variável $ ou propriedades podem ser alteradas pela instância), ex.:

O _ é uma convenção utilizada para indicar que uma propriedade é do tipo privada. Lembrando que isso é feito pois no JavaScript toda propriedade de classe é pública e pode ser acessada fora da classe sem a necessidade de utilizar o get e o set. Portanto, quando eu falei que as propriedades não podem ser utilizadas fora da classe, eu supus que os programadores irão seguir as convenções de código.

Um ponto importante, é que o $ criado da forma como o mestre Flávio fez durante o curso, ou seja:

class NegociacaoController {

  constructor() {
    let $ = document.querySelector.bind(document);
  }
}

não é uma propriedade da classe e sim uma variável que existe apenas dentro do construtor! Sendo assim não é possível fazer negociacaoController.$ = "modificado". Esse trecho de código daria erro!

2 - Considerando apenas a classe NegociacaoController entendo a tua explicação, porém comparando com a classe Negociacao volta a embaralhar: você comentou que na classe NegociacaoController não há necessidade do "freeze", porque as propriedades não são expostas para fora da mesma, mas o exemplo acima não é um exemplo de possibilidade de exposição para fora da classe? Se isso não é "exposição", não entendi qual é o escopo, o que define quando uma variável/propriedade de uma classe está exposta ou não. Complementando, se o exemplo acima não é exposição, também não entendo porque as propriedades _quantidade e _valor da classe Negociacao teriam que ter o freeze (a propriedade data entendo, por ser um objeto Date() e pode ser associado por referência à uma outra variável externa), uma vez que (pelo menos até agora) eles também parecem não estar expostos para fora da classe Negociacao...

Foi o que eu disse na resposta do ponto 1. Repare que a classe Negociacao tem os getters e setters fazendo com que o programador automaticamente saiba que por trás dos panos existem aquelas propriedades de classe e tente quebrar o encapsulamento acessando diretamente as propriedades que se iniciam com _. Esse não é o caso da classe NegociacaoController! Repare que não há nenhum get e nenhum set.

Então veja que os casos são diferentes! No caso da NegociacaoController o código está um pouco mais protegido! Entretanto, se vc quiser, vc tbm pode utilizar o Object,freeze(), não há problema nenhum nisso!

Só que para pra pensar, vc só estará fazendo esse tipo de coisa por não confiar que os programadores seguirão a convenção de não acessar propriedades privadas iniciadas com _! Então, ao meu ver, essa é uma preocupação desnecessária, afinal de contas as convenções estão aí para serem usadas e respeitadas. E se vamos partir do pressuposto que elas serão desrespeitadas, a existência delas deixa de fazer sentido!

O que estou querendo te mostrar com isso é que nesse caso, ao meu ver, seria um excesso de cuidado desnecessário utilizar o Object.freeze() quando poderíamos deixar que a convenção resolvesse o problema! Até pq, se pararmos pra pensar, até mesmo o Object.freeze() não resolve nada! Alguém pode muito bem ir lá e remover ele, desfazendo o congelamento!

Pegou a ideia? Qualquer coisa é só falar!

Grande abraço e bons estudos, meu aluno!

Olá Gabriel!

Mais uma vez, muito obrigado pela perseverança com este aluno (são poucos os que hoje em dia tem paciência para muitos questionamentos e/ou uma boa e saudável discussão).

Sobre os teus comentários:

1 - não é uma propriedade da classe e sim uma variável que existe apenas dentro do construtor! Sendo assim não é possível fazer negociacaoController.$ = "modificado". Esse trecho de código daria erro!

  • Eu entenderia se ocorresse erro, porém testei novamente e para mim o código continua executando sem erro...

2 - Repare que a classe Negociacao tem os getters e setters fazendo com que o programador automaticamente saiba que por trás dos panos existem aquelas propriedades de classe e tente quebrar o encapsulamento acessando diretamente as propriedades que se iniciam com _ . Esse não é o caso da classe NegociacaoController! Repare que não há nenhum get e nenhum set.

Só que para pra pensar, vc só estará fazendo esse tipo de coisa por não confiar que os programadores seguirão a convenção de não acessar propriedades privadas iniciadas com _! Então, ao meu ver, essa é uma preocupação desnecessária, afinal de contas as convenções estão aí para serem usadas e respeitadas. E se vamos partir do pressuposto que elas serão desrespeitadas, a existência delas deixa de fazer sentido!

  • Porque na classe NegociacaoController os programadores seguiriam a convenção do _ (e por isso o freeze não é necessário) e na classe Negociacao eles não seguiriam esta convenção (e por isso o freeze é necessário)?

At.te

solução!

Eu entenderia se ocorresse erro, porém testei novamente e para mim o código continua executando sem erro...

Me perdoe, Elías! Eu não me expressei da melhor forma!

Quando eu disse que haveria um erro é que na realidade o código não funcionaria da forma como vc espera que funcione! Teste o seguinte código aí!

<!-- index.html -->

<script src="js/NegociacaoController.js"></script>
<script>
                let negociacaoController = new NegociacaoController();

                console.log(negociacaoController);
                negociacaoController.$ = "modificado";
                console.log(negociacaoController);
</script>
// js/NegociacaoController.js
class NegociacaoController {

    constructor() {
      let $ = document.querySelector.bind(document);
    }
}

Vc irá constatar que quando vc faz negociacaoController.$ = "modificado", na realidade não é o $ de dentro do construtor que vc estará modificando.

Porque na classe NegociacaoController os programadores seguiriam a convenção do _ (e por isso o freeze não é necessário) e na classe Negociacao eles não seguiriam esta convenção (e por isso o freeze é necessário)?

Daí voltamos na minha resposta anterior onde eu disse que vc poderia utilizar o Object.freeze() tbm na NegociacaoController. No entanto, já que há a convenção, eu deixaria por conta dela resolver a questão! Inclusive, o JavaScript nunca teve uma forma de tornar os atributos de classe privados e nem por isso nos códigos que vemos sendo utilizados na vida real o pessoal sai tacando o Object.freeze() dando preferência justamente a fazer isso que eu sugeri de deixar a convenção resolver a nossa questão!

Entretanto, uma outra coisa importante a ser observada é que o objeto de NegociacaoController tem por objetivo gerenciar a tela onde o usuário interage. Ou seja, a princípio, nenhuma outra parte do código precisará ficar acessando as informações internas dessa classe.

Contudo, como eu disse antes, nada te impede de sair fazendo Object.freeze() por toda parte!

A boa notícia é que há uma proposta de incluir na linguagem JavaScript a possibilidade de tornar privadas as propriedades de classe, quem sabe essa proposta não vinga nas próximas versões do ES... Vamos ficar no aguardo!

Como sempre, qualquer coisa é só falar!

Grande abraço, meu aluno!

Olá Gabriel!

Imagina, tranquilo! Talvez eu que já deveria ter entendido huahuahua

Vc irá constatar que quando vc faz negociacaoController.$ = "modificado", 
na realidade não é o $ de dentro do construtor que vc estará modificando.
  • exato, era essa a minha dúvida! Se um dev que desconhece o código por acaso fizer isso, o código/sistema já existente vai continuar funcionando, e este dev vai precisar conferir o código e aí irá entender e achar outra forma (seja lá o que ele estiver tentando fazer).
Entretanto, uma outra coisa importante a ser observada é que o objeto de 
NegociacaoController tem por objetivo gerenciar a tela onde o usuário interage. 
Ou seja, a princípio, nenhuma outra parte do código precisará ficar acessando as 
informações internas dessa classe.
  • é, eu tinha ficado na dúvida porque nas aulas, em uma classe foi utilizado o freeze, e na outra não, não sei tu entende, pra iniciante isso parece não ter sentido.. Mas com esta tua última resposta (em especial o parágrado acima) entendi! uhuulll!
Como sempre, qualquer coisa é só falar!
  • cara não fala isso, não incentiva que dúvida/pergunta aqui não falta (obs: claro, antes sempre procuro olhar no fórum todas as dúvidas já existentes da respectiva atividade)!

Gabriel, mais uma vez muito obrigado pela persistência neste aluno! Não sabe como isso revigora as energias/ânimo para continuar estudando!

Abraço! At.te

Por nada, Elías! Estamos aqui para crescermos juntos e sempre rumar para a luz do conhecimento!

Grande abraço e bons estudos, meu aluno!