De que é feito o JavaScript?

Durante os primeiros anos de uso do JavaScript, me senti um impostor. Embora eu pudesse criar sites usando estruturas, senti que estava faltando alguma coisa. As entrevistas em JavaScript me inspiraram medo, porque eu não tinha uma compreensão clara dos conceitos básicos desse idioma.



Ao longo dos anos, construí um modelo JavaScript mental que me dá uma sensação de confiança. Aqui vou compartilhar com você uma versão muito concisa deste modelo. Sua estrutura se assemelha a um dicionário. Cada conceito é descrito em várias frases.

Ao ler este material, tente avaliar mentalmente como você se sente confiante sobre cada questão discutida aqui. E se acontecer que muitas coisas daqui não lhe parecem particularmente familiares, não vou culpá-lo por isso. Mas se isso for verdade, há algo no final do material que o ajudará a corrigir a situação.

Modelo mental JavaScript


  • Valor. O conceito de significado é um pouco abstrato. É "alguma coisa". Um valor em JavaScript é o mesmo que um número em matemática ou um ponto na geometria. Quando o seu programa é executado, o mundo deste programa está cheio de significados. Números como 1 , 2 e 420 são valores. Mas significados são outras entidades. Por exemplo, a frase "Cows go moo" . É verdade que nem tudo é significado. Um número é um valor, mas uma if não é mais um valor. Abaixo falaremos sobre diferentes tipos de valores.

    • Tipo de valor. Existem vários "tipos" de valores. Por exemplo, números - como 420 , strings - como "Cows go moo" , objetos. Existem outros tipos de valores. Você pode descobrir o tipo de valor usando o operador typeof . Por exemplo, o comando console.log(typeof 2) produzirá um number para o console.
    • Valores primitivos. Alguns valores têm tipos "primitivos". Estes são números, seqüências de caracteres e alguns outros significados. Uma propriedade interessante dos valores primitivos é que você não pode criar mais valores do que existem no idioma, não pode alterar os valores primitivos existentes. Por exemplo, sempre que você usar o número 2 no código, ele terá o mesmo valor 2 . É impossível "criar" mais um valor 2 no programa ou fazer 2 "transformar" em 3 . Isso também é verdade para seqüências de caracteres.
    • Os valores são null e undefined . Estes são dois significados especiais. Eles não são como os outros, devido ao fato de que muitas coisas não podem ser feitas com eles - sua aparência geralmente leva a erros. Geralmente, usar null é uma indicação de que algum valor não foi atribuído intencionalmente a uma variável e undefined indica que algum valor está ausente por acaso. No entanto, o programador decide como usar esses valores exatamente. Esses valores existem devido ao fato de que às vezes é melhor que ocorra um erro durante a execução de uma determinada operação e não ocorre que a execução do programa continue após o "processamento" de um valor inexistente.
  • Igualdade Como o conceito de significado, o conceito de igualdade é um dos conceitos fundamentais do JavaScript. Estamos dizendo que dois valores são iguais se eles ... de fato, não vou dizer isso. Se dois valores forem iguais, isso significa que eles são um e o mesmo valor. Não dois significados diferentes, mas um! Por exemplo, as igualidades "Cows go moo" === "Cows go moo" e 2 === 2 verdadeiras. E aqui está tudo claro: 2 é 2 . Observe que usamos três sinais de igual, que representam o conceito acima de igualdade de valores em JavaScript.

    • Igualdade estrita. Acabamos de falar sobre ele no parágrafo anterior.
    • Igualdade de links. E nós também conversamos sobre ele.
    • Igualdade desigual. Ah, mas isso é algo completamente diferente. Em JavaScript, a verificação da igualdade de valores não estrita é realizada usando um operador que consiste em dois sinais de igual ( == ). As entidades podem ser reconhecidas como iguais entre si, mesmo que sejam representadas por significados diferentes que se pareçam (algo como 2 e "2" ). Um operador de igualdade não estrito foi adicionado ao JavaScript nos estágios iniciais do desenvolvimento da linguagem, por conveniência. Desde então, tem sido uma fonte sem fundo de confusão. O conceito de igualdade frouxa não pode ser chamado de fundamental, mas é uma fonte típica de erros. Você pode aprender o operador de igualdade não estrito em um dia chuvoso, mas muitos simplesmente tentam não usar o operador == .
  • Literal. Os literais são usados ​​quando um valor é referenciado, escrevendo-o no código do programa. Por exemplo, 2 é um literal numérico e "Banana" é um literal de sequência.
  • Variável. Variáveis ​​permitem que você faça referência a valores usando nomes. Por exemplo, let message = "Cows go moo" . Após uma construção semelhante ter sido usada no código, sempre que você precisar da frase "Cows go moo" , você poderá escrever apenas uma message , em vez de repetir esta frase. Mais tarde, você pode alterar a message , fazendo a variável apontar para outra coisa. Por exemplo, usando esta construção: message = "I am the walrus" . Observe que isso não altera o significado em si. Isso afeta apenas a que a variável se refere. É como "conectar" um nome de variável a outra coisa. No início, a variável estava “conectada” a "Cows go moo" e agora a "I am the walrus" .

    • O escopo da variável. Se apenas uma variável com a message nome pudesse ser usada em todo o programa, isso seria muito ruim. Quando declaramos uma variável, ela está disponível apenas em alguma parte do programa. Esta parte é chamada de "escopo da variável". Existem regras que descrevem os recursos do escopo. Geralmente, você pode identificar o escopo de uma variável descobrindo em qual bloco delimitado por chaves ( {} ) é declarado. Esse bloco também pode ser chamado de escopo da variável.
    • Atribuindo valores a variáveis. Quando escrevemos message = "I am the walrus" no código, isso leva ao fato de alterarmos a variável de message para que aponte para o valor "I am the walrus" . Essa operação é chamada de atribuir uma variável a um valor, ou escrever algo em uma variável ou definir uma variável.
    • As palavras-chave let , const e var . Normalmente, let é a melhor palavra-chave para declarar variáveis. Se você deseja garantir que nada de novo possa ser gravado em uma variável, use a palavra-chave const . (Em algumas bases de código e comandos, esse problema é pedante, forçando todos, se o valor for gravado na variável apenas uma vez, use const .) Tente não usar a palavra-chave var , pois com variáveis ​​declaradas com ela, regras relacionadas à definição do escopo das variáveis.
  • Digite Object . O tipo de Object , as entidades pertencentes às quais são chamados objetos, desempenha um papel especial no JavaScript. Uma característica notável dos objetos é que eles podem ser associados a outros valores. Por exemplo, o objeto {flavor: "vanilla"} possui uma propriedade flavor que aponta para o valor de "vanilla" . Os objetos podem ser percebidos como valores independentes, a partir dos quais links para outros valores são desenhados.

    • Propriedade do objeto. Uma propriedade é algo como uma "conexão" que vem de um objeto e indica um determinado valor. Isso pode lembrá-lo da idéia de uma variável: uma propriedade tem um nome (como flavor ), aponta para um valor (como "vanilla" ). Mas, diferentemente de uma variável, as propriedades “vivem” dentro do próprio objeto, e não em algum lugar do código (em um determinado escopo da variável). Uma propriedade é considerada parte do objeto e o valor referenciado pela propriedade não é considerado parte do objeto.
    • Literal de objeto. Um literal de objeto é um mecanismo que permite criar objetos introduzindo construções apropriadas no código. Por exemplo, é {} ou {flavor: "vanilla"} . Entre colchetes, muitos pares da : form : , separados por vírgulas, podem ser declarados. Isso nos permite especificar os valores referenciados pelas propriedades dos objetos.
    • A identidade dos objetos. Já dissemos que 2 é igual a 2 (em outras palavras - 2 === 2 ), pois onde quer que escrevamos o número 2 , “chamamos” o mesmo valor para esse local. Mas toda vez que escrevemos {} , sempre obtemos valores diferentes. Como resultado, um objeto do formulário {} não {} igual a outro objeto, que também se parece com {} . Tente escrever o seguinte no console: {} === {} (o resultado será false ). Quando um computador encontra o número 2 no código, ele sempre funciona com o mesmo empate. Mas literais de objetos são outra coisa. Quando o computador encontra {} , ele cria um novo objeto, que é sempre o novo valor. Como verificar objetos para igualdade? O conceito de "igualdade" pode ser considerado como o conceito de "identidade de valores". Quando dizemos: “ b idênticos” - isso significa que queremos dizer que b indicam o mesmo valor (ou seja, a === b ). Quando dizemos que b não b idênticos, isso significa que b indicam valores diferentes (ou seja, a !== b ).
    • Notação de ponto. Quando você precisar ler o valor de uma propriedade de um objeto ou gravar algo em uma propriedade, poderá usar a notação de ponto ( . ). Por exemplo, se a variável iceCream apontar para um objeto cuja propriedade flavor contenha a string "chocolate" , a construção iceCream.flavor nos fornecerá "chocolate" .
    • Notação de suporte. Às vezes, o nome da propriedade do objeto a ser acessado não é conhecido antecipadamente. Por exemplo, às vezes você precisa ler o valor da propriedade iceCream.flavor e, às vezes, precisa ler o valor da propriedade iceCream.flavor . A notação entre colchetes ( [] ) permite acessar as propriedades dos objetos, definindo seus nomes usando variáveis. Por exemplo, suponha que exista uma variável no código: let ourProperty = 'flavor' . Isso significa que um design como iceCream[ourProperty] nos dará o valor "chocolate" . Curiosamente, você pode usar notação entre parênteses ao criar objetos: { [ourProperty]: "vanilla" } .
    • Mutação. Estamos falando do fato de que um objeto muda (ou muda) se alguém escreve um novo valor em sua propriedade. Por exemplo, se criamos um objeto para let iceCream = {flavor: "vanilla"} , mais tarde podemos atribuir um novo valor à propriedade usando iceCream.flavor = "chocolate" . Observe que, mesmo se declarássemos a variável iceCream usando a palavra-chave const , ela ainda não nos impediria de alterar a propriedade do objeto iceCream.flavor . Isso ocorre porque o uso de const protege apenas a variável iceCream de ser substituída e iceCream a propriedade de flavor do objeto referenciado pela variável. Algumas pessoas se recusaram a usar const apenas porque essa palavra-chave é capaz de enganar o programador.
    • Matriz Uma matriz é um objeto que é uma coleção de certos valores. As matrizes podem ser declaradas usando literais de matriz, por exemplo, assim: ["banana", "chocolate", "vanilla"] . O uso dessa construção leva à criação de um objeto cuja propriedade com o nome 0 aponta para a cadeia "banana" , propriedade 1 - para a cadeia "chocolate" , propriedade 2 - para o valor "vanilla" . Seria tedioso escrever a mesma coisa assim: {0: ..., 1: ..., 2: ...} . Portanto, matrizes são estruturas úteis. As matrizes têm mecanismos internos projetados para trabalhar com seus elementos. Entre eles estão o map , o filter e reduce métodos de reduce . Não desanime se o nome reduce parecer confuso para você. Parece incompreensível para todos.
    • Protótipo. O que acontece se você tentar acessar um objeto que não existe? Por exemplo, o que acontece se iceCream.taste e o objeto tiver apenas a propriedade flavor ? Se respondermos a essa pergunta sem entrar em detalhes, podemos dizer que, se tentarmos voltar para uma propriedade inexistente, obteremos um valor especial de undefined . Se você der uma resposta detalhada a essa pergunta, precisará começar com o fato de que a maioria dos objetos em JavaScript possui o chamado "protótipo". O protótipo de um objeto pode ser percebido como uma propriedade "oculta" que informa ao sistema onde procurar a propriedade solicitada, se não estiver no próprio objeto. Em nosso exemplo, quando o objeto iceCream não tiver uma propriedade de taste , o JavaScript procurará essa propriedade no protótipo do objeto, que também é um objeto. E se ele não encontrar lá, no protótipo do protótipo e assim por diante. Um valor undefined será .taste somente quando o final da cadeia de protótipos for atingido e a propriedade .taste não for encontrada. Você raramente precisa trabalhar diretamente com esse mecanismo, mas, conhecendo os protótipos, pode entender por que o objeto iceCream tem um método toString que nunca declaramos. Este método é retirado do protótipo do objeto.
  • Função Uma função é um significado especial que existe com o único objetivo de representar uma parte do código do programa. As funções são convenientes em situações em que o programador não deseja escrever constantemente o mesmo código. Uma "chamada" para uma função que se parece com sayHi() informa ao computador que ele precisa executar o código dentro da função e retornar ao local em que a função foi chamada. O JavaScript tem várias maneiras de declarar funções ligeiramente diferentes.

    • Argumentos (ou parâmetros) da função. Os argumentos permitem passar certos dados para a função a partir do local em que a função é chamada. Por exemplo, pode ser assim: sayHi("Amelie") . O comportamento dos argumentos em uma função é semelhante ao comportamento das variáveis. As palavras "parâmetros" e "argumentos" são usadas dependendo do que exatamente está sendo discutido - sobre a declaração de uma função ou sobre sua chamada. Embora a diferença na terminologia seja precisa - na prática, esses termos são usados ​​de forma intercambiável.
    • Expressão funcional. Anteriormente, escrevemos valores de string em variáveis. Por exemplo, let message = "I am the walrus" . Como se vê, uma função também pode ser escrita em uma variável: let sayHi = function() { } . O que vem depois do sinal = é chamado de expressão funcional. Isso nos dá um significado especial (função), que é um pedaço de código. Se precisarmos executar esse código, podemos chamar a função correspondente.
    • Declaração de função. Um programador pode estar cansado de escrever constantemente algo como let sayHi = function() { } . function sayHi() { } caso, um formulário mais curto para descrever uma função pode ser usado aqui: function sayHi() { } . Essa construção é chamada de declaração de função. Em vez de especificar um nome de variável no lado esquerdo da expressão, colocamos esse nome após a palavra-chave function . Geralmente, os dois estilos para criar funções descritas acima são intercambiáveis.
    • Elevando funções para o topo do escopo. Normalmente, uma variável só pode ser usada depois de ter sido declarada usando let ou const , abaixo do local de sua declaração. No caso de funções, isso pode ser inconveniente. As funções podem se chamar. Descobrir qual deles deve ser criado primeiro pode ser uma tarefa assustadora. O bom é que, ao usar declarações de função (e somente ao usar essa abordagem!), A ordem na qual as funções são descritas não é importante. O fato é que, com essa abordagem, as funções "aumentam" para a parte superior do escopo. Ou seja, acontece que as funções, mesmo quando você tenta chamá-las do código que vem antes da declaração, já estão definidas e prontas para o trabalho.
    • Esta palavra-chave. Talvez a palavra this chave this seja um conceito de JavaScript que geralmente é mal interpretado. Essa palavra-chave pode ser comparada a um argumento de função especial. Mas nós mesmos não transferimos suas funções. JavaScript passa. O valor this depende de como a função é chamada. Por exemplo, ao chamar um método de objeto usando a notação de ponto, como iceCream.eat() , this indicará o que está à frente do ponto. No nosso exemplo, este é um objeto iceCream . O valor this em uma função depende de como a função é chamada e não de onde foi declarada. Existem métodos especiais, como .bind , .call e .apply , que dão ao programador a capacidade de controlar o que .apply com this .
    • Funções de seta. As funções de seta se parecem com expressões funcionais. Eles são declarados assim: let sayHi = () => { } . Eles são compactos e geralmente são usados ​​para projetos de linha única. Os recursos das funções de seta são mais limitados do que os recursos das funções convencionais. Por exemplo, eles não têm a palavra this chave this . Quando a palavra-chave this é usada em uma função de seta, ela é retirada da função na qual a função de seta está incorporada. Isso é semelhante a chamar um argumento ou uma variável de uma função aninhada em outra função. Na prática, isso significa que as funções de seta são usadas quando desejam que o mesmo valor existente no código ao seu redor seja visível.
    • Vinculando this valor a funções. Geralmente, vincular uma determinada função f a um valor específico this e a um determinado conjunto de argumentos significa que uma nova função é criada que chama a função f com esses valores predefinidos. O JavaScript possui um mecanismo auxiliar para funções de ligação - o método .bind , mas você pode vinculá- this a funções de outras maneiras. A ligação era uma maneira popular de fazer com que as funções aninhadas "vissem" o mesmo valor que as funções externas a elas. Agora, as funções de seta são usadas em uma situação semelhante, como resultado, a ligação de função é usada com pouca frequência em nosso tempo.
    • Pilha de chamadas Chamar uma função é como entrar em uma sala. Cada vez que chamamos uma função, as variáveis ​​dentro dela são inicializadas novamente. Como resultado, cada chamada de função é como construir uma nova “sala” com um código de função. Quando a “sala” é “construída”, é “inserida” nela, o código da função é executado. Variáveis ​​declaradas em uma função "ao vivo" nesta "sala". Quando o retorno da função é realizado, a “sala” desaparece junto com todo o seu conteúdo. Todas essas "salas" criadas por chamadas de função podem ser representadas como uma "torre" alta. Esta é uma pilha de chamadas. Quando saímos de uma determinada função, chegamos à função, que está localizada "abaixo" dela na pilha de chamadas.
    • Recursão. Recursão é quando uma função se chama. Essa técnica é útil nos casos em que o que já foi feito pela função precisa ser repetido, mas usando outros argumentos. Por exemplo, se escrevermos um mecanismo de pesquisa que pesquisa sites, podemos ter a função collectLinks(url) . Essa função primeiro coleta os links localizados na página de um site e depois se autodenomina, passando cada um dos links encontrados para si.Isso acontece até que todas as páginas de um site sejam visitadas. O perigo da recursão é que, por acidente, você pode escrever uma função que se chamará indefinidamente. É verdade que, se o programa realmente tiver uma recursão infinita, isso causará um estouro da pilha de chamadas e a execução do programa será interrompida com um erro stack overflow. A pilha transborda devido ao fato de que muitas entradas sobre funções chamadas caem nela.
    • . — , , . , , — . — , — , , . , .
    • . () — , JavaScript. , , . : . . , setTimeout , … -. , . — . « », , .
    • . , , , , . - , . ? - . — . . , - . JavaScript, , . «». JavaScript, , , , .


JavaScript , . . JavaScript. , . JavaScript. — Just JavaScript . , , JavaScript.

! JavaScript?

Source: https://habr.com/ru/post/pt482472/


All Articles