Cómo usar los argumentos y parámetros de ES6

Publicado: 2022-03-10
Resumen rápido ↬ Los desarrolladores utilizan las funciones de ECMAScript 6 cada vez más y pronto estas funciones serán inevitables. En este tutorial, aprenderá cómo ECMAScript 6 ha mejorado el manejo de parámetros en JavaScript y más.

ECMAScript 6 (o ECMAScript 2015) es la versión más reciente del estándar ECMAScript y ha mejorado notablemente el manejo de parámetros en JavaScript. Ahora podemos usar parámetros de descanso, valores predeterminados y desestructuración, entre otras características nuevas.

En este tutorial, exploraremos argumentos y parámetros en detalle y veremos cómo ECMAScript 6 los ha actualizado.

Argumentos versus parámetros

Los argumentos y los parámetros a menudo se denominan indistintamente. No obstante, a los efectos de este tutorial, haremos una distinción. En la mayoría de los estándares, los parámetros (o parámetros formales) son lo que se da en la declaración de la función, y los argumentos (o parámetros reales) son lo que se pasa a la función. Considere esta función:

 function foo(param1, param2) { // do something } foo(10, 20);

En esta función, param1 y param2 son parámetros de función y los valores pasados ​​a la función ( 10 y 20 ) son argumentos.

Operador de propagación (…)

En ECMAScript 5, el método apply() es una herramienta conveniente para pasar una matriz como argumentos a una función. Por ejemplo, se usa comúnmente con el método Math.max() para encontrar el valor más alto en una matriz. Considere este fragmento de código:

 var myArray = [5, 10, 50]; Math.max(myArray); // Error: NaN Math.max.apply(Math, myArray); // 50

El método Math.max() no admite matrices; solo acepta números. Cuando se pasa una matriz a la función Math.max() , arroja un error. Pero cuando se usa el método apply() , la matriz se envía como números individuales, por lo que el método Math.max() puede manejarlo.

¡Más después del salto! Continúe leyendo a continuación ↓

Afortunadamente, con la introducción del operador de propagación en ECMAScript 6, ya no necesitamos usar el método apply() . Con el operador de propagación, podemos expandir fácilmente una expresión en múltiples argumentos:

 var myArray = [5, 10, 50]; Math.max(...myArray); // 50

Aquí, el operador de dispersión expande myArray para crear valores individuales para la función. Si bien es posible simular el operador de distribución mediante apply() en ECMAScript 5, la sintaxis es confusa y carece de la flexibilidad del operador de distribución. El operador de propagación no solo es más fácil de usar, sino que incluye más funciones. Por ejemplo, se puede usar varias veces y se puede mezclar con otros argumentos en una llamada de function :

 function myFunction() { for(var i in arguments){ console.log(arguments[i]); } } var params = [10, 15]; myFunction(5, ...params, 20, ...[25]); // 5 10 15 20 25

Otra ventaja del operador de propagación es que se puede usar fácilmente con constructores:

 new Date(...[2016, 5, 6]); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)

Por supuesto, podríamos reescribir el código anterior en ECMAScript 5, pero necesitaríamos usar un patrón complicado para evitar obtener un error de tipo:

 new Date.apply(null, [2016, 4, 24]); // TypeError: Date.apply is not a constructor new (Function.prototype.bind.apply(Date, [null].concat([2016, 5, 6]))); // Mon Jun 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)

Compatibilidad con navegadores de operadores extendidos en llamadas de funciones

Navegadores de escritorio:

Cromo Firefox explorador de Internet Borde de Microsoft Ópera Safari
46 27 Soportado 7.1

Navegadores móviles:

Chrome para Android Firefox móvil safari móvil Ópera Móvil IE móvil
46 27 8

Parámetros de descanso

El parámetro rest tiene la misma sintaxis que el operador de propagación, pero en lugar de expandir una matriz en parámetros, recopila parámetros y los convierte en una matriz.

 function myFunction(...options) { return options; } myFunction('a', 'b', 'c'); // ["a", "b", "c"]

Si no hay argumentos, el parámetro resto se establecerá en una matriz vacía:

 function myFunction(...options) { return options; } myFunction(); // []

Un parámetro de descanso es particularmente útil cuando se crea una función variádica (una función que acepta un número variable de argumentos). Al tener la ventaja de ser matrices, los parámetros de descanso pueden reemplazar fácilmente el objeto de arguments (que explicaremos más adelante en este tutorial). Considere esta función, escrita en ECMAScript 5:

 function checkSubstrings(string) { for (var i = 1; i < arguments.length; i++) { if (string.indexOf(arguments[i]) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true

Esta función comprueba si una cadena contiene varias subcadenas. El primer problema con esta función es que tenemos que mirar dentro del cuerpo de la function para ver que toma múltiples argumentos. El segundo problema es que la iteración debe comenzar desde 1 en lugar de 0 , porque arguments[0] apunta al primer argumento. Si luego decidimos agregar otro parámetro antes o después de la cadena, es posible que nos olvidemos de actualizar el ciclo. Con el resto de parámetros, evitamos fácilmente estos problemas:

 function checkSubstrings(string, ...keys) { for (var key of keys) { if (string.indexOf(key) === -1) { return false; } } return true; } checkSubstrings('this is a string', 'is', 'this'); // true

La salida de esta función es la misma que la anterior. Aquí nuevamente, la string parámetros se llena con el argumento que se pasa primero, pero el resto de los argumentos se colocan en una matriz y se asignan a las keys variables.

Usar el parámetro rest en lugar del objeto arguments mejora la legibilidad del código y evita problemas de optimización en JavaScript. Sin embargo, el parámetro de descanso no está exento de limitaciones. Por ejemplo, debe ser el último argumento; de lo contrario, se producirá un error de sintaxis:

 function logArguments(a, ...params, b) { console.log(a, params, b); } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter

Otra limitación es que solo se permite un parámetro de descanso en la declaración de la function :

 function logArguments(...param1, ...param2) { } logArguments(5, 10, 15); // SyntaxError: parameter after rest parameter

Compatibilidad con el navegador de parámetros de reposo

Navegadores de escritorio:

Cromo Firefox explorador de Internet Borde de Microsoft Ópera Safari
47 15 Soportado 34

Navegadores móviles:

Chrome para Android Firefox móvil safari móvil Ópera Móvil IE móvil
47 15

Parámetros predeterminados

Parámetros predeterminados en ECMAScript 5

JavaScript no admite parámetros predeterminados en ECMAScript 5, pero existe una solución sencilla. Usando un operador OR lógico ( || ) dentro de la función, podemos simular fácilmente los parámetros predeterminados en ECMAScript 5. Considere esta función:

 function foo(param1, param2) { param1 = param1 || 10; param2 = param2 || 10; console.log(param1, param2); } foo(5, 5); // 5 5 foo(5); // 5 10 foo(); // 10 10

Esta función espera dos argumentos, pero cuando se llama sin argumentos, utilizará los valores predeterminados. Dentro de la función, los argumentos que faltan se establecen automáticamente como indefinidos; entonces, podemos detectar estos argumentos y declarar valores predeterminados para ellos. Para detectar argumentos faltantes y establecer valores predeterminados, usamos el operador lógico OR ( || ). Este operador examina su primer argumento: si es verdadero, el operador lo devuelve; si no es así, el operador devuelve su segundo argumento.

Este enfoque se usa comúnmente en funciones, pero tiene una falla. Pasar 0 o null también activará un valor predeterminado, porque estos se consideran valores falsos. Entonces, si realmente necesitamos pasar 0 o null a esta función, necesitaríamos una forma alternativa de verificar si falta un argumento:

 function foo(param1, param2) { if(param1 === undefined){ param1 = 10; } if(param2 === undefined){ param2 = 10; } console.log(param1, param2); } foo(0, null); // 0, null foo(); // 10, 10

Dentro de esta función, los tipos de argumentos pasados ​​se verifican para asegurarse de que no estén definidos antes de que se asignen los valores predeterminados. Este enfoque requiere solo un poco más de código, pero es una alternativa más segura y nos permite pasar 0 y null a la función.

Parámetros predeterminados en ECMAScript 6

Con ECMAScript 6, ya no necesitamos buscar valores indefinidos para simular parámetros predeterminados. Ahora podemos poner los valores predeterminados directamente en la declaración de la function :

 function foo(a = 10, b = 10) { console.log(a, b); } foo(5); // 5 10 foo(0, null); // 0 null

Como puede ver, omitir un argumento activa el valor predeterminado, pero pasar 0 o null no lo hará. Incluso podemos usar funciones para recuperar valores para parámetros predeterminados:

 function getParam() { alert("getParam was called"); return 3; } function multiply(param1, param2 = getParam()) { return param1 * param2; } multiply(2, 5); // 10 multiply(2); // 6 (also displays an alert dialog)

Tenga en cuenta que la función getParam solo se llama si se omite el segundo argumento. Entonces, cuando llamamos a la función multiply() con dos parámetros, la alerta no se mostrará.

Otra característica interesante de los parámetros predeterminados es que podemos hacer referencia a otros parámetros y variables en la declaración de la function :

 function myFunction(a=10, b=a) { console.log('a = ' + a + '; b = ' + b); } myFunction(); // a=10; b=10 myFunction(22); // a=22; b=22 myFunction(2, 4); // a=2; b=4

Incluso puede realizar operaciones en la declaración de la function :

 function myFunction(a, b = ++a, c = a*b) { console.log(c); } myFunction(5); // 36

Tenga en cuenta que, a diferencia de otros lenguajes, JavaScript evalúa los parámetros predeterminados en el momento de la llamada:

 function add(value, array = []) { array.push(value); return array; } add(5); // [5] add(6); // [6], not [5, 6]

Compatibilidad con el explorador de parámetros predeterminado

Navegadores de escritorio:

Rasgo Cromo Firefox explorador de Internet Borde de Microsoft Ópera Safari
Soporte básico 49 15 14
Parámetros sin valores predeterminados después del parámetro predeterminado 49 26 14

Navegadores móviles:

Rasgo Chrome para Android Firefox móvil safari móvil Ópera Móvil IE móvil
Soporte básico 49 15
Parámetros sin valores predeterminados después del parámetro predeterminado 46 26

desestructuración

La desestructuración es una característica nueva en ECMAScript 6 que nos permite extraer valores de matrices y objetos y asignarlos a variables usando una sintaxis que es similar a los literales de objetos y matrices. La sintaxis es clara y fácil de entender y es particularmente útil cuando se pasan argumentos a una función.

En ECMAScript 5, un objeto de configuración se usa a menudo para manejar una gran cantidad de parámetros opcionales, especialmente cuando el orden de las propiedades no importa. Considere esta función:

 function initiateTransfer(options) { var protocol = options.protocol, port = options.port, delay = options.delay, retries = options.retries, timeout = options.timeout, log = options.log; // code to initiate transfer } options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true }; initiateTransfer(options);

Los desarrolladores de JavaScript suelen utilizar este patrón y funciona bien, pero tenemos que mirar dentro del cuerpo de la function para ver qué parámetros espera. Con parámetros desestructurados, podemos indicar claramente los parámetros en la declaración de la function :

 function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer }; var options = { protocol: 'http', port: 800, delay: 150, retries: 10, timeout: 500, log: true } initiateTransfer(options);

En esta función, hemos utilizado un patrón de desestructuración de objetos, en lugar de un objeto de configuración. Esto hace que nuestra función no solo sea más concisa, sino también más fácil de leer.

También podemos combinar parámetros desestructurados con regulares:

 function initiateTransfer(param1, {protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer('some value', options);

Tenga en cuenta que se generará un error de tipo si se omiten los parámetros en la llamada a la function :

 function initiateTransfer({protocol, port, delay, retries, timeout, log}) { // code to initiate transfer } initiateTransfer(); // TypeError: Cannot match against 'undefined' or 'null'

Este es el comportamiento deseado cuando necesitamos que se requieran parámetros, pero ¿qué pasa si queremos que sean opcionales? Para evitar este error cuando faltan parámetros, debemos asignar un valor predeterminado a los parámetros desestructurados:

 function initiateTransfer({protocol, port, delay, retries, timeout, log} = {}) { // code to initiate transfer } initiateTransfer(); // no error

En esta función, se proporciona un objeto vacío como valor predeterminado para los parámetros desestructurados. Ahora, si se llama a esta función sin ningún parámetro, no se producirá ningún error.

También podemos asignar un valor por defecto a cada parámetro desestructurado:

 function initiateTransfer({ protocol = 'http', port = 800, delay = 150, retries = 10, timeout = 500, log = true }) { // code to initiate transfer }

En este ejemplo, cada propiedad tiene un parámetro predeterminado, lo que elimina la necesidad de que verifiquemos manualmente los parámetros no definidos y asignemos valores predeterminados dentro del cuerpo de la function .

Desestructuración del soporte del navegador

Navegadores de escritorio:

Rasgo Cromo Firefox explorador de Internet Borde de Microsoft Ópera Safari
Soporte básico 49 2.0 14 7.1
Parámetro desestructurado con asignación de valor por defecto 49 47 14

Navegadores móviles:

Rasgo Chrome para Android Firefox móvil safari móvil Ópera Móvil IE móvil
Soporte básico 49 1 8
Parámetros sin valores predeterminados después del parámetro predeterminado 49 47

pasar argumentos

Hay dos formas de pasar argumentos a una función: por referencia o por valor. La modificación de un argumento que se pasa por referencia se refleja globalmente, pero la modificación de un argumento que se pasa por valor solo se refleja dentro de la función.

En algunos lenguajes, como Visual Basic y PowerShell, tenemos la opción de especificar si pasar un argumento por referencia o por valor, pero ese no es el caso con JavaScript.

Pasar argumentos por valor

Técnicamente, JavaScript solo puede pasar por valor. Cuando pasamos un argumento a una función por valor, se crea una copia de ese valor dentro del alcance de la function . Por lo tanto, cualquier cambio en el valor se refleja solo dentro de la function . Considere este ejemplo:

 var a = 5; function increment(a) { a = ++a; console.log(a); } increment(a); // 6 console.log(a); // 5

Aquí, modificar el argumento dentro de la función no tiene efecto en el valor original. Entonces, cuando la variable se registra desde fuera de la función, el valor impreso sigue siendo 5 .

Pasar argumentos por referencia

En JavaScript, todo se pasa por valor, pero cuando pasamos una variable que hace referencia a un objeto (incluidas las matrices), el "valor" es una referencia al objeto, y cambiar una propiedad de un objeto al que hace referencia una variable cambia el objeto subyacente.

Considere esta función:

 function foo(param){ param.bar = 'new value'; } obj = { bar : 'value' } console.log(obj.bar); // value foo(obj); console.log(obj.bar); // new value

Como puede ver, la propiedad del objeto se modifica dentro de la función, pero el valor modificado es visible fuera de la función.

Cuando pasamos un valor no primitivo, como una matriz u objeto, detrás de escena se crea una variable que apunta a la ubicación del objeto original en la memoria. Esta variable luego se pasa a la función, y modificarla afectará el objeto original.

Comprobación de tipos y parámetros extra o faltantes

En un lenguaje fuertemente tipado, tenemos que especificar el tipo de parámetros en la declaración de la function , pero JavaScript carece de esta característica. En JavaScript, no importa qué tipo de datos o cuántos argumentos pasemos a una función.

Supongamos que tenemos una función que acepta solo un argumento. Cuando llamamos a esa función, no estamos limitados a pasar solo un argumento a la función; ¡somos libres de pasar uno, dos o más argumentos! Incluso podemos optar por no pasar nada en absoluto, y no se producirán errores.

El número de argumentos y parámetros puede diferir de dos maneras:

  • Menos argumentos que parámetros .
    Los parámetros que faltan serán iguales a undefined .
  • Más argumentos que parámetros .
    Los parámetros adicionales se ignorarán, pero se pueden recuperar a través de los argumentos variables especiales tipo matriz (que se analizan a continuación).

Argumentos Obligatorios

Si falta un argumento en una llamada de function , se establecerá en undefined . Podemos aprovechar este comportamiento y arrojar un error si se omite un argumento:

 function foo(mandatory, optional) { if (mandatory === undefined) { throw new Error('Missing parameter: mandatory'); } }

En ECMAScript 6, podemos llevar esto más allá y usar parámetros predeterminados para establecer argumentos obligatorios:

 function throwError() { throw new Error('Missing parameter'); } function foo(param1 = throwError(), param2 = throwError()) { // do something } foo(10, 20); // ok foo(10); // Error: missing parameter

Objeto Argumentos

El soporte para parámetros de descanso se agregó a ECMAScript 4 con la intención de reemplazar el objeto de arguments , pero ECMAScript 4 nunca llegó a buen término. Con el lanzamiento de ECMAScript 6, JavaScript ahora admite oficialmente el resto de parámetros. También rechazó el plan de eliminar el soporte para el objeto de arguments .

El objeto arguments es un objeto similar a una matriz que está disponible en todas las funciones. Permite que los valores del argument pasados ​​a la función se recuperen por número, en lugar de por nombre. El objeto nos permite pasar cualquier número de argumentos a una función. Considere el siguiente fragmento de código:

 function checkParams(param1) { console.log(param1); // 2 console.log(arguments[0], arguments[1]); // 2 3 console.log(param1 + arguments[0]); // 2 + 2 } checkParams(2, 3);

Esta función espera recibir solo un argumento. Cuando lo llamamos con dos argumentos, el primer argumento es accesible en la función por el nombre de parámetro param1 o el objeto arguments[0] , pero el segundo argumento solo es accesible como arguments[1] . Además, tenga en cuenta que el objeto arguments se puede usar junto con argumentos con nombre.

El objeto arguments contiene una entrada para cada argumento pasado a la función y el índice de la primera entrada comienza en 0 . Si quisiéramos acceder a más argumentos en el ejemplo anterior, escribiríamos arguments[2] , arguments[3] y así sucesivamente.

Incluso podríamos omitir la configuración de parámetros con nombre por completo y simplemente usar el objeto de arguments :

 function checkParams() { console.log(arguments[1], arguments[0], arguments[2]); } checkParams(2, 4, 6); // 4 2 6

De hecho, los parámetros con nombre son una conveniencia, no una necesidad. De manera similar, los parámetros de descanso se pueden usar para reflejar los argumentos pasados:

 function checkParams(...params) { console.log(params[1], params[0], params[2]); // 4 2 6 console.log(arguments[1], arguments[0], arguments[2]); // 4 2 6 } checkParams(2, 4, 6);

El objeto arguments es un objeto similar a una matriz, pero carece de métodos de matriz como slice() y foreach() . Para usar métodos de matriz en el objeto de arguments , primero se debe convertir el objeto en una matriz real:

 function sort() { var a = Array.prototype.slice.call(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]

En esta función, Array.prototype.slice.call() se usa como una forma rápida de convertir el objeto de arguments en una matriz. A continuación, el método sort() ordena los elementos de la matriz y los devuelve.

ECMAScript 6 tiene una forma aún más sencilla. Array.from() , una nueva adición en ECMAScript 6, crea una nueva matriz a partir de cualquier objeto similar a una matriz:

 function sort() { var a = Array.from(arguments); return a.sort(); } sort(40, 20, 50, 30); // [20, 30, 40, 50]

La propiedad de longitud

Aunque el objeto arguments no es técnicamente una matriz, tiene una propiedad de length que se puede usar para verificar la cantidad de argumentos que se pasan a una función:

 function countArguments() { console.log(arguments.length); } countArguments(); // 0 countArguments(10, null, "string"); // 3

Al usar la propiedad de length , tenemos un mejor control sobre la cantidad de argumentos que se pasan a una función. Por ejemplo, si una función requiere dos argumentos para funcionar, podríamos usar la propiedad de length para verificar la cantidad de argumentos pasados ​​y arrojar un error si son menos de lo esperado:

 function foo(param1, param2) { if (arguments.length < 2) { throw new Error("This function expects at least two arguments"); } else if (arguments.length === 2) { // do something } }

Los parámetros de descanso son matrices, por lo que tienen una propiedad de length . En ECMAScript 6, el código anterior se puede reescribir con parámetros de descanso:

 function foo(...params) { if (params.length < 2) { throw new Error("This function expects at least two arguments"); } else if (params.length === 2) { // do something } }

Las propiedades del destinatario y del llamante

La callee de la persona que llama se refiere a la función que se está ejecutando actualmente, y la persona que caller se refiere a la función que ha llamado a la función que se está ejecutando actualmente. En el modo estricto de ECMAScript 5, estas propiedades están obsoletas y al intentar acceder a ellas se produce un TypeError.

La propiedad arguments.callee es útil en funciones recursivas (una función recursiva es una función regular que se refiere a sí misma por su nombre), especialmente cuando el nombre de la función no está disponible (una función anónima). Debido a que una función anónima no tiene nombre, la única forma de referirse a ella es arguments.callee .

 var result = (function(n) { if (n <= 1) { return 1; } else { return n * arguments.callee(n - 1); } })(4); // 24

Objeto de argumentos en modos estrictos y no estrictos

En el modo no estricto de ECMAScript 5, el objeto arguments tiene una característica inusual: mantiene sus valores sincronizados con los valores de los parámetros con nombre correspondientes.

Considere el siguiente fragmento de código:

 function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // true return param } foo(200); // 500

Dentro de esta función, se asigna un nuevo valor a arguments[0] . Debido a que los valores de los arguments siempre están sincronizados con los valores de los parámetros con nombre, el cambio a arguments[0] también cambiará el valor de param . De hecho, son como dos nombres diferentes para la misma variable. En el modo estricto de ECMAScript 5, se eliminó este comportamiento confuso del objeto arguments :

 "use strict"; function foo(param) { console.log(param === arguments[0]); // true arguments[0] = 500; console.log(param === arguments[0]); // false return param } foo(200); // 200

Esta vez, cambiar arguments[0] no afecta a param y el resultado es el esperado. El resultado de esta función en ECMAScript 6 es el mismo que en el modo estricto de ECMAScript 5, pero tenga en cuenta que cuando se utilizan valores predeterminados en la declaración de la function , el objeto de arguments no se ve afectado:

 function foo(param1, param2 = 10, param3 = 20) { console.log(param1 === arguments[0]); // true console.log(param2 === arguments[1]); // true console.log(param3 === arguments[2]); // false console.log(arguments[2]); // undefined console.log(param3); // 20 } foo('string1', 'string2');

En esta función, aunque param3 tiene un valor predeterminado, no es igual a arguments[2] porque solo se pasan dos argumentos a la función. En otras palabras, establecer valores predeterminados no tiene efecto en el objeto de arguments .

Conclusión

ECMAScript 6 ha traído cientos de pequeñas y grandes mejoras a JavaScript. Cada vez más, los desarrolladores utilizan las funciones de ECMAScript 6 y pronto estas funciones serán inevitables. En este tutorial, hemos aprendido cómo ECMAScript 6 ha mejorado el manejo de parámetros en JavaScript, pero solo hemos arañado la superficie de ECMAScript 6. Vale la pena revisar muchas otras características nuevas e interesantes del lenguaje.

Enlaces

  • Tabla de compatibilidad de ECMAScript 6, Juriy Zaytsev
  • "ECMAScript 2015 Language Specification", ECMA International