6 Maneiras de Declarar Funções JavaScript

Uma função é um bloco paramétrico de código definido uma vez e chamado várias vezes mais tarde. Em JavaScript uma função é composta e influenciada por muitos componentes:

  • código JavaScript que forma o corpo da função
  • A lista de parâmetros
  • As variáveis acessíveis a partir do âmbito léxico
  • O valor retornado
  • O contexto this quando a função é invocada
  • Nome ou uma função anónima
  • A variável que detém o objecto da função
  • arguments objecto (ou em falta numa função de seta)

p> Este post ensina-lhe seis abordagens para declarar funções JavaScript: a sintaxe, exemplos e armadilhas comuns. Além disso, compreenderá quando utilizar um tipo específico de função em determinadas circunstâncias.

1. Declaração de função

Uma declaração de função é feita de function palavra-chave, seguida de um nome de função obrigatório, uma lista de parâmetros num par de parênteses (para1, ..., paramN) e um par de chaves de caracóis {...} que delimita o código do corpo.

Um exemplo de declaração de função:

// function declarationfunction isEven(num) { return num % 2 === 0;}isEven(24); // => trueisEven(11); // => false

function isEven(num) {...} é uma declaração de função que define isEven função, que determina se um número é par.

A declaração de função cria uma variável no âmbito actual com o identificador igual ao nome da função. Esta variável detém o objecto da função.

A variável função é içada até ao topo do âmbito actual, o que significa que a função pode ser invocada antes da declaração (ver este capítulo para mais detalhes).

A função criada é nomeada, o que significa que o name propriedade do objecto da função detém o seu nome. É útil ao visualizar a pilha de chamadas: na depuração ou na leitura de mensagens de erro.

Vejamos estas propriedades num exemplo:

// Hoisted variableconsole.log(hello('Aliens')); // => 'Hello Aliens!'// Named functionconsole.log(hello.name) // => 'hello'// Variable holds the function objectconsole.log(typeof hello); // => 'function'function hello(name) { return `Hello ${name}!`;}

A declaração da função function hello(name) {...} cria uma variável hello que é içada para o topo do âmbito actual. hello variável detém o objecto da função e hello.name contém o nome da função: 'hello'.

1.1 Uma função regular

A declaração da função corresponde aos casos em que é necessária uma função regular. Regular significa que declara a função uma vez e mais tarde invoca-a em muitos lugares diferentes. Este é o cenário básico:

function sum(a, b) { return a + b;}sum(5, 6); // => 11().reduce(sum) // => 10

Porque a declaração de função cria uma variável no âmbito actual, juntamente com as chamadas de função regulares, é útil para a repetição ou destacamento de ouvintes de eventos. Ao contrário das expressões de função ou funções de seta, que não criam uma ligação com a variável de função pelo seu nome.

Por exemplo, para calcular recursivamente o factorial, tem de aceder à função dentro:

function factorial(n) { if (n === 0) { return 1; } return n * factorial(n - 1);}factorial(4); // => 24

Inside factorial() está a ser feita uma chamada recursiva utilizando a variável que detém a função: factorial(n - 1).

É possível usar uma expressão de função e atribuí-la a uma variável regular, por exemplo const factorial = function(n) {...}. Mas a declaração de função function factorial(n) é compacta (não há necessidade de const e =).

Uma propriedade importante da declaração de função é o seu mecanismo de elevação. Ela permite utilizar a função antes da declaração no mesmo âmbito.

Hoisting é útil em algumas situações. Por exemplo, quando se gostaria de ver como a função é chamada no início de um script, sem ler a implementação da função. A implementação da função pode ser localizada abaixo no ficheiro, pelo que nem sequer se pode deslocar até lá.

Pode ler mais detalhes sobre a declaração da função içada aqui.

1.2 Diferença da expressão da função

É fácil confundir a declaração da função com a expressão da função. Têm um aspecto muito semelhante mas produzem funções com propriedades diferentes.

Uma regra fácil de lembrar: a declaração da função numa declaração começa sempre com a palavra-chave function. Caso contrário, é uma expressão de função (ver 2.).

A amostra seguinte é uma declaração de função em que a declaração começa com function palavra-chave:

// Function declaration: starts with "function"function isNil(value) { return value == null;}

No caso de expressões de função, a declaração JavaScript não começa com function palavra-chave (está presente algures no meio do código da declaração):

// Function expression: starts with "const"const isTruthy = function(value) { return !!value;};// Function expression: an argument for .filter()const numbers = ().filter(function(item) { return typeof item === 'number';});// Function expression (IIFE): starts with "("(function messageFunction(message) { return message + ' World!';})('Hello');

1.3 Declaração de função em condições

alguns ambientes JavaScript podem lançar um erro de referência ao invocar uma função cuja declaração aparece dentro de blocos {...} de iffor ou while declarações.
Vamos activar o modo restrito e ver o que acontece quando uma função é declarada num condicional:

(function() { 'use strict'; if (true) { function ok() { return 'true ok'; } } else { function ok() { return 'false ok'; } } console.log(typeof ok === 'undefined'); // => true console.log(ok()); // Throws "ReferenceError: ok is not defined"})();

Ao chamar ok(), o JavaScript lança ReferenceError: ok is not defined, porque a declaração da função está dentro de um bloco condicional.

A declaração de função em condições é permitida em modo não condicional, o que a torna ainda mais confusa.

Como regra geral para estas situações, quando uma função deve ser criada por condições – usar uma expressão de função. Vejamos como é possível:

(function() { 'use strict'; let ok; if (true) { ok = function() { return 'true ok'; }; } else { ok = function() { return 'false ok'; }; } console.log(typeof ok === 'function'); // => true console.log(ok()); // => 'true ok'})();

Porque a função é um objecto regular, atribui-la a uma variável dependendo da condição. A invocação ok() funciona bem, sem erros.

2. Expressão da função

Uma expressão da função é determinada por uma function palavra-chave, seguida por um nome opcional da função, uma lista de parâmetros num par de parênteses (para1, ..., paramN) e um par de chaves de caracóis { ... } que delimita o código do corpo.

algumas amostras da expressão da função:

const count = function(array) { // Function expression return array.length;}const methods = { numbers: , sum: function() { // Function expression return this.numbers.reduce(function(acc, num) { // func. expression return acc + num; }); }}count(); // => 3methods.sum(); // => 14

A expressão da função cria um objecto de função que pode ser utilizado em diferentes situações:

  • Atribuído a uma variável como objecto count = function(...) {...}
  • Cria um método sobre um objecto sum: function() {...}
  • Utilizar a função como um cavalo de trabalho em JavaScript. Normalmente, lida com este tipo de declaração de função, juntamente com a função de seta (se preferir uma sintaxe curta e um contexto lexical).

    2.1 Expressão de função nomeada

    Uma função é anónima quando não tem nome (name propriedade é uma cadeia vazia ''):

    ( function(variable) {return typeof variable; }).name; // => ''

    Esta é uma função anónima, cujo nome é uma cadeia vazia.

    Por vezes, o nome da função pode ser inferido. Por exemplo, quando o anónimo é atribuído a uma variável:

    const myFunctionVar = function(variable) { return typeof variable; };myFunctionVar.name; // => 'myFunctionVar'

    O nome da função anónima é 'myFunctionVar', porque myFunctionVar o nome da variável é utilizado para inferir o nome da função.

    Quando a expressão tem o nome especificado, esta é uma expressão de função nomeada. Tem algumas propriedades adicionais em comparação com uma simples expressão de função:

    • É criada uma função nomeada, ou seja name propriedade detém o nome da função
    • Dentro do corpo da função uma variável com o mesmo nome detém o objecto da função

    Vamos utilizar o exemplo acima, mas definir um nome na expressão da função:

    const getType = function funName(variable) { console.log(typeof funName === 'function'); // => true return typeof variable;}console.log(getType(3)); // => 'number'console.log(getType.name); // => 'funName'console.log(typeof funName); // => 'undefined'

    function funName(variable) {...} é uma expressão de função nomeada. A variável funName é acessível dentro do âmbito da função, mas não fora. De qualquer modo, a propriedade name do objecto da função detém o nome: funName.

    2.2 Expressão da função nomeada por favor

    Quando uma expressão da função const fun = function() {} é atribuída a uma variável, alguns motores inferem o nome da função a partir desta variável. No entanto, as chamadas de retorno podem ser passadas como expressões anónimas de função, sem armazenamento em variáveis: por isso, o motor não pode determinar o seu nome.

    É razoável favorecer funções nomeadas e evitar funções anónimas para obter benefícios como:

    • As mensagens de erro e pilhas de chamadas mostram informações mais detalhadas quando se utilizam os nomes das funções
    • Depuração mais confortável, reduzindo o número de nomes de pilhas anónimas
    • O nome da função diz o que a função faz
    • Pode aceder à função dentro do seu âmbito para chamadas recursivas ou destacamento de ouvintes de eventos

    3. Shorthand method definition

    Shorthand method definition pode ser usado numa declaração de método sobre literals de objectos e classes ES2015. Pode defini-los usando um nome de função, seguido por uma lista de parâmetros num par de parênteses (para1, ..., paramN) e um par de chaves de caracóis { ... } que delimita as declarações do corpo.

    O exemplo seguinte utiliza uma definição de método abreviado num objecto literal:

    const collection = { items: , add(...items) { this.items.push(...items); }, get(index) { return this.items; }};collection.add('C', 'Java', 'PHP');collection.get(1) // => 'Java'

    add() e get() métodos em collection objecto são definidos utilizando uma definição curta do método. Estes métodos são chamados como habitualmente: collection.add(...) e collection.get(...).

    A abordagem curta da definição do método tem vários benefícios sobre a definição de propriedade tradicional com um nome, dois pontos : e uma expressão de função add: function(...) {...}:

    • Uma sintaxe mais curta é mais fácil de compreender
    • A definição do método de corthand cria uma função nomeada, ao contrário de uma expressão de função. É útil para depuração.

    O class a sintaxe requer declarações de método de forma curta:

    class Star { constructor(name) { this.name = name; } getMessage(message) { return this.name + message; }}const sun = new Star('Sun');sun.getMessage(' is shining') // => 'Sun is shining'

    3.1 Nomes e métodos de propriedade calculados

    ECMAScript 2015 acrescenta uma característica agradável: nomes de propriedade calculados em literals e classes de objectos.
    As propriedades computadas utilizam uma sintaxe ligeiramente diferente () {...}, pelo que a definição do método tem este aspecto:

    const addMethod = 'add', getMethod = 'get';const collection = { items: , (...items) { this.items.push(...items); }, (index) { return this.items; }};collection('C', 'Java', 'PHP');collection(1) // => 'Java'

    (...) {...} e (...) {...} são declarações abreviadas do método com nomes de propriedades computadas.

    4. Função seta

    Uma função seta é definida usando um par de parênteses que contém a lista de parâmetros (param1, param2, ..., paramN), seguido por uma seta gorda => e um par de chaves de caracóis {...} que delimita as declarações do corpo.

    Quando a função de seta tem apenas um parâmetro, o par de parênteses pode ser omitido. Quando contém uma única declaração, o par de parênteses também pode ser omitido.

    p>Vejamos o uso básico da função de seta:

    const absValue = (number) => { if (number < 0) { return -number; } return number;}absValue(-10); // => 10absValue(5); // => 5

    absValue é uma função de seta que calcula o valor absoluto de um número.

    A função declarada usando uma seta gorda tem as seguintes propriedades:

    • A função de seta não cria o seu contexto de execução, mas toma-a lexicalmente (ao contrário da expressão da função ou declaração da função, que cria a própria this dependendo da invocação)
    • A função de seta é anónima. Contudo, o motor pode inferir o seu nome a partir da variável que detém a função.
    • arguments objecto não está disponível na função de seta (ao contrário de outros tipos de declaração que fornecem arguments objecto). É livre de utilizar parâmetros de repouso (...params), no entanto.

    4.1 Transparência do contexto

    this palavra-chave é um aspecto confuso do JavaScript (verifique este artigo para uma explicação detalhada sobre this).
    Porque as funções criam um contexto de execução próprio, muitas vezes é difícil detectar this valor.

    ECMAScript 2015 melhora this utilização introduzindo a função de seta, que toma o contexto lexicalmente (ou simplesmente usa this a partir do âmbito exterior imediato). Isto é bom porque não tem de usar .bind(this) ou armazenar o contexto var self = this quando uma função precisa do contexto envolvente.

    vejamos como this é herdado da função exterior:

    class Numbers { constructor(array) { this.array = array; } addNumber(number) { if (number !== undefined) { this.array.push(number); } return (number) => { console.log(this === numbersObject); // => true this.array.push(number); }; }}const numbersObject = new Numbers();const addMethod = numbersObject.addNumber();addMethod(1);addMethod(5);console.log(numbersObject.array); // => 

    Numbers classe contém um conjunto de números e fornece um método addNumber() para inserir novos números.
    Quando addNumber() é chamado sem argumentos, é devolvido um fecho que permite inserir números. Este fecho é uma função de seta que tem this como numbersObject instância porque o contexto é tomado lexicamente de addNumbers() método.

    Sem a função de seta, é necessário fixar manualmente o contexto. Significa utilizar soluções de trabalho como .bind() método:

    //... return function(number) { console.log(this === numbersObject); // => true this.array.push(number); }.bind(this);//...

    ou armazenar o contexto numa variável separada var self = this:

    //... const self = this; return function(number) { console.log(self === numbersObject); // => true self.array.push(number); };//...

    A transparência do contexto pode ser usada quando se pretende manter this como está, retirado do contexto envolvente.

    4.2 Curtas chamadas de retorno

    Quando se cria uma função de seta, os pares de parênteses e os parênteses curvos são opcionais para um único parâmetro e uma única declaração de corpo. Isto ajuda na criação de funções de revogação muito curtas.

    Vamos fazer uma função que descobre se uma matriz contém 0:

    const numbers = ;numbers.some(item => item === 0); // => true

    item => item === 0 é uma função de seta que parece directa.

    Nota que as funções de seta curta aninhadas são difíceis de ler. A forma mais conveniente de utilizar a forma de função de seta curta é uma única chamada de retorno (sem aninhamento).

    Se necessário, utilizar a sintaxe expandida das funções de seta ao escrever funções de seta aninhada. É simplesmente mais fácil de ler.

    5. Função Gerador

    A função gerador em JavaScript retorna um objecto Gerador. A sua sintaxe é semelhante à expressão de função, declaração de função ou declaração de método, apenas que requer um carácter estrela *.

    A função geradora pode ser declarada nas seguintes formas:

    a. Formulário de declaração da função function* <name>():

    function* indexGenerator(){ var index = 0; while(true) { yield index++; }}const g = indexGenerator();console.log(g.next().value); // => 0console.log(g.next().value); // => 1

    b. Forma de expressão da função function* ():

    const indexGenerator = function* () { let index = 0; while(true) { yield index++; }};const g = indexGenerator();console.log(g.next().value); // => 0console.log(g.next().value); // => 1

    c. Formulário de definição do método abreviado *<name>():

    const obj = { *indexGenerator() { var index = 0; while(true) { yield index++; } }}const g = obj.indexGenerator();console.log(g.next().value); // => 0console.log(g.next().value); // => 1

    Nos 3 casos a função gerador devolve o objecto gerador g. Mais tarde g é usado para gerar séries de números incrementais.

    6. Mais uma coisa: nova Função

    Em JavaScript as funções são objectos de primeira classe – uma função é um objecto regular do tipo function.
    As formas da declaração descrita acima criam o mesmo tipo de objecto de função. Vejamos um exemplo:

    function sum1(a, b) { return a + b;}const sum2 = function(a, b) { return a + b;}const sum3 = (a, b) => a + b;console.log(typeof sum1 === 'function'); // => trueconsole.log(typeof sum2 === 'function'); // => trueconsole.log(typeof sum3 === 'function'); // => true

    O tipo de objecto função tem um construtor: Function.
    Quando Function é invocado como construtor new Function(arg1, arg2, ..., argN, bodyString), é criada uma nova função. Os argumentos arg1, args2, ..., argN passados para construtor tornam-se os nomes dos parâmetros para a nova função e o último argumento bodyString é usado como o código do corpo da função.

    Vamos criar uma função que soma dois números:

    const numberA = 'numberA', numberB = 'numberB';const sumFunction = new Function(numberA, numberB, 'return numberA + numberB');sumFunction(10, 15) // => 25

    sumFunction criado com Function construtor invocação tem parâmetros numberA e numberB e o corpo return numberA + numberB.

    As funções criadas desta forma não têm acesso ao âmbito actual, pelo que os fechos não podem ser criados. Elas são sempre criadas no âmbito global.

    Uma possível aplicação de new Function é uma melhor forma de aceder ao objecto global num browser ou script NodeJS:

    (function() { 'use strict'; const global = new Function('return this')(); console.log(global === window); // => true console.log(this === window); // => false})();

    Lembrem-se que as funções quase nunca devem ser declaradas utilizando new Function(). Porque o corpo da função é avaliado em tempo de execução, esta abordagem herda muitos eval() problemas de utilização: riscos de segurança, depuração mais difícil, nenhuma forma de aplicar optimizações do motor, nenhum editor auto-completar.

    7. No final, qual a melhor forma?

    não há vencedor ou perdedor. A decisão sobre o tipo de declaração a escolher depende da situação.

    Existem no entanto algumas regras que pode seguir em situações comuns.

    Se a função utilizar this da função envolvente, a função seta é uma boa solução. Quando a função de retorno tem uma declaração curta, a função de seta é também uma boa opção, porque cria código curto e leve.

    Para uma sintaxe mais curta ao declarar métodos sobre objectos literais, é preferível a declaração do método abreviado.

    new Function a forma de declarar funções normalmente não deve ser utilizada. Principalmente porque abre potenciais riscos de segurança, não permite o auto-completar do código nos editores e perde as optimizações do motor.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *