Recursos JavaScript pouco conhecidos

O JavaScript é frequentemente chamado de linguagem mais simples para iniciantes, em programação que é a mais difícil de obter domínio. O autor do material, cuja tradução publicamos, diz que não pode deixar de concordar com esta afirmação. O problema é que o JS é uma linguagem muito antiga e muito flexível. Está cheio de misteriosas construções sintáticas e características obsoletas que ainda suporta.

imagem

Hoje falaremos sobre os recursos e opções pouco conhecidos do JavaScript para sua aplicação prática.

JavaScript é sempre algo novo


Trabalho com JavaScript há muitos anos e constantemente me deparo com algo que nunca suspeitei que existisse. Aqui, tentei listar recursos pouco conhecidos semelhantes da linguagem. No modo estrito, alguns deles não funcionarão, mas no modo normal, são amostras de código JS completamente corretas. Note-se que não pretendo aconselhar os leitores a colocar tudo isso em serviço. Embora o que eu vou falar pareça muito interessante para você, você pode começar a usar tudo isso se trabalhar em equipe e, para dizer o mínimo, surpreender seus colegas.

→ O código que discutiremos aqui pode ser encontrado aqui.

Observe que não incluí coisas como aumento de variáveis, fechamentos, objetos proxy, herança de protótipo, assíncrono / espera, geradores e similares. Embora esses recursos da linguagem possam ser atribuídos a difíceis de entender, eles não são bem conhecidos.

Operador de anulação


JavaScript tem um operador void unário. Você pode ter encontrado ele na forma de void(0) ou void 0 . Seu único objetivo é calcular a expressão à sua direita e retornar undefined . 0 aqui é usado simplesmente porque é habitual, embora isso não seja necessário, e aqui você pode usar qualquer expressão válida. É verdade que, em qualquer caso, esse operador retornará undefined .

 //  void void 0                  // undefined void (0)                // undefined void 'abc'              // undefined void {}                 // undefined void (1 === 1)          // undefined void (1 !== 1)          // undefined void anyfunction()      // undefined 

Por que adicionar uma palavra-chave especial ao idioma que serve para retornar undefined , se você pode apenas usar o valor padrão undefined ? Não é, existe alguma redundância?

Como se viu, antes da aparência do padrão ES5 na maioria dos navegadores, um valor padrão de undefined poderia receber um novo valor. Digamos que você possa executar com sucesso este comando: undefined = "abc" . Como resultado, um valor undefined pode não ser o que deveria ser. Naqueles dias, o uso de void nos permitiu garantir a confiança no uso do real undefined .

Parênteses ao chamar construtores são opcionais


Os colchetes que são adicionados após o nome da classe, invocando o construtor, são completamente opcionais (a menos que o construtor precise passar argumentos).

No exemplo a seguir, a presença ou ausência de parênteses não afeta a operação correta do programa.

 //     const date = new Date() const month = new Date().getMonth() const myInstance = new MyClass() //     const date = new Date const month = (new Date).getMonth() const myInstance = new MyClass 

Os suportes não podem ser usados ​​com IIFE


A sintaxe IIFE sempre me pareceu estranha. Por que existem todos esses colchetes?

Como se viu, os colchetes são necessários apenas para informar ao analisador JavaScript que algum código é uma expressão funcional e não uma tentativa incorreta de declarar uma função. Conhecer esse fato nos permite entender que existem muitas maneiras de se livrar dos colchetes nos quais o IIFE está incluído e, ao mesmo tempo, escrever código de trabalho.

 // IIFE (function () { console.log('Normal IIFE called') })() // Normal IIFE called void function () { console.log('Cool IIFE called') }() // Cool IIFE called 

Aqui, o operador void diz ao analisador que o código a seguir é uma expressão funcional. Isso torna possível se livrar dos colchetes em torno da declaração da função. A propósito, aqui você pode usar qualquer operador unário ( void , + , ! , - e assim por diante), e o código continuará funcionando. Isso não é maravilhoso?

No entanto, se você é um leitor atento, pode se perguntar se o operador unário afeta o resultado retornado pelo IIFE. De fato, do jeito que está. Mas o bom é que, se você precisar do resultado do IIFE, que você armazena em uma variável, por exemplo, não precisará de parênteses em torno do IIFE. Aqui está um exemplo.

 // IIFE,    let result = (function () { // ... -  return 'Victor Sully' })() console.log(result) // Victor Sully let result1 = function () { // ... -  return 'Nathan Drake' }() console.log(result1) // Nathan Drake 

As chaves ao redor do primeiro IIFE apenas melhoram a legibilidade do código sem afetar sua operação.

Se você deseja entender melhor o IIFE, dê uma olhada neste material.

Construção com


Você sabia que o JavaScript possui uma construção with que suporta blocos de expressão? É assim:

 with (object)  statement //       with (object) {  statement  statement  ... } 

A construção with adiciona todas as propriedades do objeto transmitidas a ele na cadeia de escopo usada ao executar os comandos.

 //    with const person = { firstname: 'Nathan', lastname: 'Drake', age: 29 } with (person) { console.log(`${firstname} ${lastname} is ${age} years old`) } // Nathan Drake is 29 years old 

with pode parecer uma ótima ferramenta. Parece que é ainda melhor do que os novos recursos de JS para destruição de objetos , mas na verdade não é.

A construção with foi descontinuada e não é recomendada para uso. No modo estrito, seu uso é proibido. Acontece que os blocos causam problemas de desempenho e segurança.

Construtor de Função


Usar a palavra-chave function não é a única maneira de definir uma nova função. Você pode definir funções dinamicamente usando o construtor Function e o new operador. Aqui está como fica.

 //  Function const multiply = new Function('x', 'y', 'return x*y') multiply(2,3) // 6 

O último argumento passado ao construtor é uma sequência com o código da função. Dois outros argumentos são parâmetros de função.

É interessante notar que o construtor Function é o "pai" de todos os construtores em JavaScript. Até o construtor Object é um construtor Function . E o construtor nativo de Function também é Function . Como resultado, uma chamada do tipo object.constructor.constructor... feita para qualquer objeto JS um número suficiente de vezes retornará o construtor Function como resultado.

Propriedades do Recurso


Todos sabemos que funções em JavaScript são objetos de primeira classe. Portanto, ninguém está nos impedindo de adicionar novas propriedades às funções. Isso é perfeitamente normal, mas isso raramente é usado.

Quando isso pode ser necessário?

De fato, existem várias situações em que esse recurso pode ser útil. Considere-os.

▍ Recursos personalizados


Suponha que tenhamos uma função greet() . Precisamos que ela exiba diferentes mensagens de boas-vindas, dependendo das configurações regionais usadas. Essas configurações podem ser armazenadas em uma variável externa à função. Além disso, a função pode ter uma propriedade que define essas configurações, em particular as configurações de idioma do usuário. Usaremos a segunda abordagem.

 //  ,   function greet () { if (greet.locale === 'fr') {   console.log('Bonjour!') } else if (greet.locale === 'es') {   console.log('Hola!') } else {   console.log('Hello!') } } greet() // Hello! greet.locale = 'fr' greet() // Bonjour! 

WithFunções com variáveis ​​estáticas


Aqui está outro exemplo semelhante. Suponha que precisamos implementar um certo gerador que produz uma sequência de números ordenados. Normalmente, nessas situações, para armazenar informações sobre o último número gerado, são utilizadas variáveis ​​de contador estático nas classes ou IIFE. Com essa abordagem, restringimos o acesso ao balcão e evitamos a poluição do escopo global com variáveis ​​adicionais.

Mas e se precisarmos de flexibilidade, se precisarmos ler ou mesmo modificar o valor desse contador e não entupir o escopo global?

Obviamente, você pode criar uma classe com a variável correspondente e com métodos que permitem trabalhar com ela. Ou você não pode se preocupar com essas coisas e apenas usar as propriedades das funções.

 //  ,   function generateNumber () { if (!generateNumber.counter) {   generateNumber.counter = 0 } return ++generateNumber.counter } console.log(generateNumber()) // 1 console.log(generateNumber()) // 2 console.log('current counter value: ', generateNumber.counter) // current counter value: 2 generateNumber.counter = 10 console.log('current counter value: ', generateNumber.counter) // current counter value: 10 console.log(generateNumber()) // 11 

Propriedades do objeto Arguments


Estou certo de que a maioria de vocês sabe que funções têm um objeto de arguments . Este é um objeto semelhante a um array acessível em todas as funções (com exceção das funções de seta, que não possuem seus próprios objetos de arguments ). Ele contém uma lista de argumentos passados ​​para a função quando foi chamada. Além disso, possui algumas propriedades interessantes:

  • arguments.callee contém um link para a função atual.
  • arguments.caller contém uma referência à função que chamou a função atual.

Considere um exemplo.

 //  callee  caller  arguments const myFunction = function () { console.log('Current function: ', arguments.callee.name) console.log('Invoked by function: ', arguments.callee.caller.name) } void function main () { myFunction() } () // Current function: myFunction // Invoked by function: main 

O padrão ES5 proíbe o uso de propriedades de caller e caller no modo estrito, mas elas ainda são amplamente encontradas em muitos textos de programas compilados por JavaScript, por exemplo, em bibliotecas. Portanto, é útil saber sobre eles.

Literais de modelo com a tag


Certamente você, se tiver algo a ver com a programação JavaScript, já ouviu falar em literais de modelos . Literais de modelo são uma das muitas grandes inovações do padrão ES6. No entanto, você sabe sobre literais de modelo com tags?

 //    `Hello ${username}!` //    myTag`Hello ${username}!` 

Os literais de modelo com tags permitem que o desenvolvedor controle como o literal do modelo se transforma em uma sequência. Isso é feito usando tags especiais. Uma tag é apenas o nome de uma função de analisador que recebe uma matriz de cadeias e valores interpretados por um padrão de cadeia. Ao usar uma função de tag, espera-se que ele retorne a string final.

No exemplo a seguir, nossa tag, highlight , interpreta os dados de um literal de modelo e os incorpora em uma linha finalizada, colocando-os na tag HTML <mark> para selecioná-los quando esse texto é exibido em uma página da web.

 //    function highlight(strings, ...values) { //  i -      let result = '' strings.forEach((str, i) => {   result += str   if (values[i]) {     result += `<mark>${values[i]}</mark>`   } }) return result } const author = 'Henry Avery' const statement = `I am a man of fortune & I must seek my fortune` const quote = highlight`${author} once said, ${statement}` // <mark>Henry Avery</mark> once said, <mark>I am a man of fortune // & I must seek my fortune</mark> 

Maneiras interessantes de usar esse recurso podem ser encontradas em muitas bibliotecas. Aqui estão alguns exemplos:


Getters e Setters no ES5


Objetos JavaScript, na maioria das vezes, são bastante simples. Suponha que tenhamos um objeto de user e estamos tentando acessar sua propriedade age usando a construção user.age . Com essa abordagem, se essa propriedade for definida, obteremos seu valor e, se não for definido, ficaremos undefined . Tudo é muito simples.

Mas trabalhar com propriedades não precisa ser tão primitivo. Objetos JS implementam o conceito de getters e setters. Em vez de retornar diretamente o valor de alguma propriedade do objeto, podemos escrever nossa própria função getter, que retorna o que consideramos necessário. O mesmo se aplica à gravação de novos valores nas propriedades usando as funções do setter.

Os getters e setters permitem implementar esquemas avançados para trabalhar com propriedades. Ao ler ou escrever propriedades, você pode usar os conceitos de campos virtuais, verificar os valores dos campos e, ao escrever ou ler, podem ocorrer alguns efeitos colaterais úteis.

 //    const user = { firstName: 'Nathan', lastName: 'Drake', // fullname -    get fullName() {   return this.firstName + ' ' + this.lastName }, //      set age(value) {   if (isNaN(value)) throw Error('Age has to be a number')   this._age = Number(value) }, get age() {   return this._age } } console.log(user.fullName) // Nathan Drake user.firstName = 'Francis' console.log(user.fullName) // Francis Drake user.age = '29' console.log(user.age) // 29 // user.age = 'invalid text' // Error: Age has to be a number 

Getters e setters não são inovações padrão do ES5. Eles estavam sempre presentes no idioma. No ES5, apenas ferramentas de sintaxe convenientes foram adicionadas para trabalhar com elas. Detalhes sobre getters e setters podem ser encontrados aqui .

Exemplos do uso de getters incluem a popular biblioteca Node.j Colors .

Esta biblioteca estende a classe String e adiciona muitos métodos getter a ela. Isso permite converter uma string em sua versão "colorida" para que essa string possa ser usada para registro. Isso é feito trabalhando com propriedades de sequência.

Operador de vírgula


JS tem um operador de vírgula. Permite escrever várias expressões em uma única linha, separadas por vírgula e retornar o resultado da avaliação da última expressão. Aqui está a aparência de tais projetos.

 let result = expression1, expression2,... expressionN 

Aqui, os valores de todas as expressões serão calculados, após o qual o valor da expressãoN entrará na variável de result .

É possível que você já tenha usado o operador de vírgula for loops.

 for (var a = 0, b = 10; a <= 10; a++, b--) 

Às vezes, esse operador é muito útil quando você precisa escrever várias expressões na mesma linha.

 function getNextValue() {   return counter++, console.log(counter), counter } 

Pode ser útil ao projetar pequenas funções de seta.

 const getSquare = x => (console.log (x), x * x) 

Operador Plus


Se você precisar transformar rapidamente uma sequência em um número, o operador positivo será útil. Ele é capaz de trabalhar com uma variedade de números, e não apenas, como pode parecer, com números positivos. Estamos falando de números negativos, octais, hexadecimais e números em notação exponencial. Além disso, ele pode converter objetos Date e Moment.js na biblioteca de data e hora.

 //  "" +'9.11'          // 9.11 +'-4'            // -4 +'0xFF'          // 255 +true            // 1 +'123e-5'        // 0.00123 +false           // 0 +null            // 0 +'Infinity'      // Infinity +'1,234'         // NaN +new Date      // 1542975502981 ( ) +momentObject    // 1542975502981 ( ) 

Ponto de exclamação duplo


Note-se que o que às vezes é chamado de "operador de ponto de exclamação duplo" (Bang Bang ou Double Bang) não é, de fato, um operador. Este é um operador lógico NOT ou um operador de negação lógica que se parece com um ponto de exclamação repetido duas vezes. O ponto de exclamação duplo é bom porque permite converter qualquer expressão em um valor booleano. Se a expressão, do ponto de vista de JS, for verdadeira - após processá-la com um ponto de exclamação duplo, true será retornado. Caso contrário, false será retornado.

 //     !!null            // false !!undefined       // false !!false           // false !!true            // true !!""              // false !!"string"        // true !!0               // false !!1               // true !!{}              // true !![]              // true 

Operador de negação bit a bit


Vamos ser sinceros: ninguém se importa com operadores bit a bit. Eu não estou falando sobre usá-los. No entanto, o operador de negação bit a bit pode ser usado em muitas situações.

Quando esse operador é aplicado aos números, ele os converte da seguinte forma: a partir do número N verifica N se -(N+1) . Essa expressão fornece 0 se N for -1 .

Esse recurso pode ser usado com o método indexOf() quando é usado para verificar a existência de um elemento em uma matriz ou em uma string, pois esse método retorna -1 se o elemento não for encontrado.

 //      indexOf let username = "Nathan Drake" if (~username.indexOf("Drake")) { console.log('Access denied') } else { console.log('Access granted') } 

Note-se que nos padrões ES6 e ES7, respectivamente, para strings e matrizes, o método includes() apareceu. É definitivamente muito mais conveniente determinar a presença de elementos do que usar o operador de negação bit a bit e indexOf() .

Blocos nomeados


O JavaScript tem um conceito de rótulos, com o qual você pode atribuir nomes (rótulos) a loops. Você pode usar esses rótulos para se referir ao loop apropriado ao aplicar instruções de break ou continue . As etiquetas também podem ser atribuídas a blocos de código regulares.

Loops rotulados são úteis ao trabalhar com loops aninhados. Mas eles também podem ser usados ​​para organizar convenientemente o código em blocos ou ao criar blocos nos quais o código pode ser interrompido.

 //    declarationBlock: { //       //     var i, j } forLoop1: //     - "forLoop1" for (i = 0; i < 3; i++) {       forLoop2: //     -  "forLoop2"  for (j = 0; j < 3; j++) {       if (i === 1 && j === 1) {        continue forLoop1     }     console.log('i = ' + i + ', j = ' + j)  } } /* i = 0, j = 0 i = 0, j = 1 i = 0, j = 2 i = 1, j = 0 i = 2, j = 0 i = 2, j = 1 i = 2, j = 2 */ //      loopBlock4: { console.log('I will print') break loopBlock4 console.log('I will not print') } // I will print 

Observe que, diferentemente de outros idiomas, não existe uma goto no JS. Como resultado, os rótulos são usados ​​apenas com instruções de break e continue .

Sumário


Neste artigo, falamos sobre recursos JavaScript pouco conhecidos, cujo conhecimento é útil para qualquer programador JS, pelo menos para estar pronto para encontrar algo incomum no código de outra pessoa. Se o tópico "JS desconhecido" é interessante para você, você pode dar uma olhada nesta publicação.

Caros leitores! Se você conhece alguns recursos pouco conhecidos do JS e vê opções para sua aplicação prática, fale-nos sobre eles.

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


All Articles