Expresiones de función invocadas inmediatamente de JavaScript

E

Introducción

Definir y llamar a funciones son prácticas clave para dominar JavaScript y la mayoría de los otros lenguajes de programación. Por lo general, una función se define antes de que se llame en su código.

Las expresiones de función invocadas inmediatamente (IIFE), pronunciadas “dudoso”, son un patrón común de JavaScript que ejecuta una función instantáneamente después de que se define. Los desarrolladores utilizan principalmente este patrón para garantizar que las variables solo sean accesibles dentro del alcance de la función definida.

En este artículo, primero aprenderá sobre las expresiones de función. Después, profundizaremos más en los IIFE: cómo escribirlos y cuándo usarlos. Finalmente, discutiremos cómo let La palabra clave introducida en ECMAScript 6 proporciona una alternativa más limpia para algunos casos de uso de IIFE.

¿Qué son las expresiones de función?

En JavaScript, puede definir una función de 2 formas diferentes:

  • Una declaración
  • Una expresión

Declaraciones de funciones empezar con el function palabra clave, seguida del nombre de la función y cualquier argumento que pueda tomar. Por ejemplo, podemos crear un logName función usando una declaración como esta:

function logName(userName) {
    console.log(`${userName}, you are awesome`);
};

logName("Jane");

De la definición de la función, registramos cualquier valor dado en el message parámetro a la consola. Luego llamamos a la función con “Jane, you are great!”, Que imprimirá ese texto en la consola.

Al definir una función mediante declaraciones de función, la función se eleva. Una función o variable elevada se coloca en la parte superior de su alcance funcional cuando JavaScript está ejecutando código.

Nota: Alcance funcional se refiere al lugar donde se definió una función. Por ejemplo, si una función foo() contenía una función bar() dentro de él, diríamos que el alcance funcional de bar() es foo(). Si foo() no se definió dentro de una función, entonces foo() pertenece al ámbito global. Las funciones de JavaScript en el ámbito global son accesibles para todo el código que se ejecuta con él.

En la práctica, este comportamiento le permite utilizar una función antes de definirla. Por ejemplo, el código anterior se puede reescribir así y se comportaría de la misma manera:

logName();

function logName(name) {
    console.log(`${name}, you are awesome!`);
};

Expresiones de funciones son definiciones de funciones que se asignan a una variable. Por tanto, nuestro logName() La declaración de función puede convertirse en una expresión de función si la creamos así:

const logUserName = function logName(name) {
    console.log(`${name}, you are awesome!`);
};

logUserName("Jane");

En este ejemplo, para llamar a la función, necesitamos usar el nombre de la variable que se proporcionó: logUserName. Esto no cambia el comportamiento de la función, aún registra “Eres increíble” en la consola.

A diferencia de las declaraciones de función, las expresiones de función son no izado. Estas funciones solo están disponibles cuando el intérprete de JavaScript procesa esa línea de código.

Por ejemplo, si intentamos llamar logUserName() antes de crearlo como una expresión de función:

logUserName("Jane");
const logUserName = function logName(name) {
    console.log(`${name}, you are awesome!`);
};

Obtenemos el siguiente error:

Uncaught ReferenceError: Cannot access 'logUserName' before initialization

Otra diferencia entre las expresiones de función y las declaraciones de función es que las expresiones de función pueden definir funciones sin un nombre.

Las funciones sin nombre se denominan funciones anónimas. Por ejemplo, logUserName() también podría definirse con una función anónima como esta:

const logUserName = function (name) {
    console.log(`${name}, you are awesome!`);
};

Funciones de flecha

Las funciones de flecha proporcionan azúcar sintáctico para las expresiones de función. Una reimplementación de nuestro logUserName La función que usa una función de flecha se vería así:

const logUserName = (name) => {
    console.log(`${name}, you are awesome!`);
}

Lea Funciones de flecha en JavaScript para obtener más información sobre esta sintaxis y cómo afecta el alcance de la función.

Ahora que sabemos cómo crear varias expresiones de función, aprendamos a invocarlas inmediatamente.

¿Qué son las expresiones de función invocadas inmediatamente?

Los IIFE son funciones que se ejecutan inmediatamente después de ser definidas.

Podemos hacer que cualquier expresión de función sea un IIFE envolviéndola entre paréntesis y agregando el siguiente par de paréntesis al final:

(function() {
    // Code that runs in your function
})()

Alternativamente, puede usar la sintaxis de flecha para crear un IIFE de la siguiente manera:

(() => {
    // Code that runs in your function
})()

Los paréntesis que rodean la definición de la función le permiten a JavaScript saber que procesará una expresión de función. El último par de paréntesis invoca la función.

Variaciones de sintaxis

Puede crear IIFE sin el primer conjunto de paréntesis si usa un operador unario: caracteres especiales que le dicen a JavaScript que evalúe la siguiente expresión.

Podemos crear expresiones de función con operadores unarios como este:

+function () {
    // Code that runs in your function
}();

-function () {
    // Code that runs in your function
}();

!function () {
    // Code that runs in your function
}();

~function () {
    // Code that runs in your function
}();

void function () {
    // Code that runs in your function
}();

Es importante tener en cuenta que estos operadores podrían afectar cualquier dato devuelto por su función. Por ejemplo, el siguiente código parece que debería devolver 10, pero en realidad regresa -10:

$ node
> -function () {return 10;}();
-10
>

Dado que esta sintaxis unaria es menos común y puede resultar confusa para los desarrolladores, por lo general se desaconseja.

Los IIFE también pueden tomar argumentos funcionales. Podemos pasar variables al alcance como se muestra a continuación:

(function(arg1, arg2) {
    // Code that runs in your function
})("hello", "world");

Ahora que hemos visto cómo crear IIFE, veamos situaciones comunes en las que se utilizan.

¿Cuándo usar un IIFE?

Los casos de uso más comunes para IIFE son:

  • Alias ​​de variables globales
  • Creación de funciones y variables privadas
  • Funciones asincrónicas en bucles

Alias ​​de variables globales

Si tiene 2 bibliotecas que exportan un objeto con el mismo nombre, puede usar IIFE para asegurarse de que no entren en conflicto con su código. Por ejemplo, el jQuery y Efectivo Las bibliotecas de JavaScript exportan $ como su objeto principal.

Puede envolver su código dentro de un IIFE que pasa una de las variables globales como argumento. Digamos que queremos asegurarnos de que $ se refiere a jQuery objeto, y no el cash alternativa. Puede asegurarse de que se use jQuery con el siguiente IIFE:

(function($) {
    // Code that runs in your function
})(jQuery);

Creación de funciones y variables privadas

Podemos usar IIFE para crear variables privadas y funciones dentro del alcance global, o cualquier otro alcance de función.

Las funciones y variables agregadas al ámbito global están disponibles para todos los scripts que se cargan en una página. Digamos que tenemos una función generateMagicNumber(), que devolvió un número aleatorio entre 900 y 1000 inclusive, y una variable favoriteNumber en nuestro archivo JavaScript.

Podemos escribirlos así:

function generateMagicNumber() {
    return Math.floor(Math.random() * 100) + 900;
}

console.log("This is your magic number: " + generateMagicNumber());

var favoriteNumber = 5;
console.log("Twice your favorite number is " + favoriteNumber * 2);

Si cargamos otros archivos JavaScript en nuestro navegador, también obtienen acceso a generateMagicNumber() y favoriteNumber. Para evitar que los usen o los editen, encapsulamos nuestro código en un IIFE:

(function () {
    function generateMagicNumber() {
        return Math.floor(Math.random() * 100) + 900;
    }

    console.log("This is your magic number: " + generateMagicNumber());

    var favoriteNumber = 5;
    console.log("Twice your favorite number is " + favoriteNumber * 2);
})();

Funciona igual, pero ahora generateMagicNumber() y favoriteNumber solo son accesibles en nuestro script.

Funciones asincrónicas en bucles

El comportamiento de JavaScript sorprende a muchos cuando las devoluciones de llamada se ejecutan en bucles. Por ejemplo, contemos de 1 a 5 en JavaScript, dejando un espacio de 1 segundo entre cada vez que registramos un mensaje. Una implementación ingenua sería:

for (var i = 1; i <= 5; i++) {
    setTimeout(function () {
        console.log('I reached step ' + i);
    }, 1000 * i);
}

Si ejecuta este código, obtendrá el siguiente resultado:

$ node naiveCallbackInLoop.js
I reached step 6
I reached step 6
I reached step 6
I reached step 6
I reached step 6

Si bien la salida se imprimiría 1 segundo después de la otra, cada línea imprime que llegaron al paso 6. ¿Por qué?

Cuando JavaScript encuentra código asincrónico, pospone la ejecución de la devolución de llamada hasta que se completa la tarea asincrónica. Así es como permanece sin bloqueo. En este ejemplo, el console.log() La instrucción se ejecutará solo después de que haya transcurrido el tiempo de espera.

JavaScript también creó un cierre para nuestra devolución de llamada. Los cierres son una combinación de una función y su alcance cuando fue creado. Con cierres, nuestra devolución de llamada puede acceder a la variable i a pesar de for loop ya ha terminado de ejecutarse.

Sin embargo, nuestra devolución de llamada solo tiene acceso al valor de i en el momento de su ejecución. Como el código dentro del setTimeout() todas las funciones fueron diferidas, for El bucle se terminó con i siendo igual a 6. Es por eso que todos registran que llegaron al paso 6.

Este problema se puede resolver con un IIFE:

for (var i = 1; i <= 5; i++) {
    (function (step) {
        setTimeout(function() {
            console.log('I reached step ' + step);
        }, 1000 * i);
    })(i);
}

Al usar un IIFE, creamos un nuevo alcance para nuestra función de devolución de llamada. Nuestro IIFE toma un parámetro step. Cada vez que se llama a nuestro IIFE, le damos el valor actual de i como su argumento. Ahora, cuando la devolución de llamada esté lista para ejecutarse, su cierre tendrá el valor correcto de step.

Si ejecutamos este fragmento de código, veremos el siguiente resultado:

$ node iifeCallbackInLoop.js
I reached step 1
I reached step 2
I reached step 3
I reached step 4
I reached step 5

Si bien los IIFE resuelven nuestro problema con cambios mínimos en el código, echemos un vistazo a cómo las características de ES6 pueden facilitar la ejecución de código asincrónico en bucles.

Bloquear alcance con let y const

ES6 agregó el let y const palabras clave para crear variables en JavaScript. Variables declaradas con let o const son alcance de bloque. Esto significa que solo se puede acceder a ellos dentro de su bloque circundante, una región encerrada por llaves { }.

Vamos a contar de 1 a 5 en intervalos de 1 segundo usando el let palabra clave en lugar de var:

for (let i = 1; i <= 5; i++) {
    setTimeout(function () {
        console.log('I reached step ' + i);
    }, 1000 * i);
}

Obtendremos el siguiente resultado cuando ejecutemos este código:

$ node es6CallbackInLoop.js
I reached step 1
I reached step 2
I reached step 3
I reached step 4
I reached step 5

Ahora que la variable i tiene un alcance de bloque, los cierres para nuestra función de devolución de llamada obtienen el valor apropiado de i cuando finalmente se ejecutan. Esto es más conciso que nuestra implementación IIFE.

Utilizando let es la forma preferida de ejecutar funciones asincrónicas en un bucle,

Conclusión

Una expresión de función invocada inmediatamente (IIFE) es una función que se ejecuta instantáneamente después de su definición. Este patrón se ha utilizado para crear alias de variables globales, hacer que las variables y funciones sean privadas y garantizar que el código asincrónico en los bucles se ejecute correctamente.

Si bien es popular, hemos visto cómo los cambios en ES6 pueden eliminar la necesidad de usar IIFE en JavaScript moderno. Sin embargo, dominar este patrón también nos da una comprensión más profunda del alcance y el cierre, y será especialmente útil para mantener el código JavaScript heredado.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con tus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad