Buscar..
Introducción
Las funciones en JavaScript proporcionan un código organizado y reutilizable para realizar un conjunto de acciones. Las funciones simplifican el proceso de codificación, evitan la lógica redundante y hacen que el código sea más fácil de seguir. Este tema describe la declaración y la utilización de funciones, argumentos, parámetros, declaraciones de devolución y alcance en JavaScript.
Sintaxis
Ejemplo de función (x) {return x}
var ejemplo = función (x) {return x}
(función () {...}) (); // Expresión de función invocada inmediatamente (IIFE)
var instance = nuevo Ejemplo (x);
Métodos
fn.apply (valueForThis [, arrayOfArgs])
fn.bind (valueForThis [, arg1 [, arg2, ...]])
fn.call (valueForThis [, arg1 [, arg2, ...]])
ES2015 + (ES6 +):
ejemplo de const = x => {return x}; // Función de flecha retorno explícito
ejemplo de const = x => x; // Función de flecha de retorno implícito
ejemplo de const = (x, y, z) => {...} // Función de flecha múltiples argumentos
(() => {...}) (); // IIFE usando una función de flecha
Observaciones
Para obtener información sobre las funciones de flecha, consulte la documentación de Funciones de flecha .
Funciones como variable
Una declaración de función normal se ve así:
function foo(){
}
Una función definida como esta es accesible desde cualquier lugar dentro de su contexto por su nombre. Pero a veces puede ser útil tratar las referencias de funciones como referencias de objetos. Por ejemplo, puede asignar un objeto a una variable en función de un conjunto de condiciones y luego recuperar una propiedad de uno u otro objeto:
var name = 'Cameron';
var spouse;
if ( name === 'Taylor' ) spouse = { name: 'Jordan' };
else if ( name === 'Cameron' ) spouse = { name: 'Casey' };
var spouseName = spouse.name;
En JavaScript, puedes hacer lo mismo con las funciones:
// Example 1
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = function(value){ /*...*/ };
else if ( hashAlgorithm === 'md5' ) hash = function(value){ /*...*/ };
hash('Fred');
En el ejemplo anterior, el hash
es una variable normal. Se le asigna una referencia a una función, después de lo cual la función a la que hace referencia puede invocarse utilizando paréntesis, al igual que una declaración de función normal.
El ejemplo anterior hace referencia a funciones anónimas ... funciones que no tienen su propio nombre. También puede usar variables para referirse a funciones nombradas. El ejemplo anterior podría reescribirse así:
// Example 2
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = sha1Hash;
else if ( hashAlgorithm === 'md5' ) hash = md5Hash;
hash('Fred');
function md5Hash(value){
// ...
}
function sha1Hash(value){
// ...
}
O bien, puede asignar referencias de funciones de las propiedades del objeto:
// Example 3
var hashAlgorithms = {
sha1: function(value) { /**/ },
md5: function(value) { /**/ }
};
var hashAlgorithm = 'sha1';
var hash;
if ( hashAlgorithm === 'sha1' ) hash = hashAlgorithms.sha1;
else if ( hashAlgorithm === 'md5' ) hash = hashAlgorithms.md5;
hash('Fred');
Puede asignar la referencia a una función mantenida por una variable a otra omitiendo los paréntesis. Esto puede resultar en un error fácil de cometer: intentar asignar el valor de retorno de una función a otra variable, pero asignar accidentalmente la referencia a la función.
// Example 4
var a = getValue;
var b = a; // b is now a reference to getValue.
var c = b(); // b is invoked, so c now holds the value returned by getValue (41)
function getValue(){
return 41;
}
Una referencia a una función es como cualquier otro valor. Como ha visto, se puede asignar una referencia a una variable, y el valor de referencia de esa variable se puede asignar posteriormente a otras variables. Puede pasar las referencias a funciones como cualquier otro valor, incluida la transferencia de una referencia a una función como el valor de retorno de otra función. Por ejemplo:
// Example 5
// getHashingFunction returns a function, which is assigned
// to hash for later use:
var hash = getHashingFunction( 'sha1' );
// ...
hash('Fred');
// return the function corresponding to the given algorithmName
function getHashingFunction( algorithmName ){
// return a reference to an anonymous function
if (algorithmName === 'sha1') return function(value){ /**/ };
// return a reference to a declared function
else if (algorithmName === 'md5') return md5;
}
function md5Hash(value){
// ...
}
No es necesario asignar una referencia de función a una variable para invocarla. Este ejemplo, aprovechando el ejemplo 5, llamará a getHashingFunction y luego invocará la función devuelta y pasará su valor de retorno a hashedValue.
// Example 6
var hashedValue = getHashingFunction( 'sha1' )( 'Fred' );
Una nota sobre el alzamiento
Tenga en cuenta que, a diferencia de las declaraciones de funciones normales, las variables que hacen referencia a las funciones no están "elevadas". En el ejemplo 2, las funciones md5Hash
y sha1Hash
se definen en la parte inferior de la secuencia de comandos, pero están disponibles en todas partes inmediatamente. No importa dónde defina una función, el intérprete lo "eleva" al máximo de su alcance, lo que lo hace disponible de inmediato. Este no es el caso de las definiciones de variables, por lo que se romperá un código como el siguiente:
var functionVariable;
hoistedFunction(); // works, because the function is "hoisted" to the top of its scope
functionVariable(); // error: undefined is not a function.
function hoistedFunction(){}
functionVariable = function(){};
Función anónima
Definiendo una función anónima
Cuando se define una función, a menudo se le asigna un nombre y luego se invoca con ese nombre, de esta manera:
foo();
function foo(){
// ...
}
Cuando define una función de esta manera, el tiempo de ejecución de Javascript almacena su función en la memoria y luego crea una referencia a esa función, utilizando el nombre que le ha asignado. Ese nombre es entonces accesible dentro del alcance actual. Esta puede ser una forma muy conveniente de crear una función, pero Javascript no requiere que asigne un nombre a una función. Lo siguiente también es perfectamente legal:
function() {
// ...
}
Cuando una función se define sin un nombre, se conoce como una función anónima. La función se almacena en la memoria, pero el tiempo de ejecución no crea automáticamente una referencia a la misma para usted. A primera vista, puede parecer que tal cosa no tendría ningún uso, pero hay varios escenarios donde las funciones anónimas son muy convenientes.
Asignar una función anónima a una variable
Un uso muy común de las funciones anónimas es asignarlas a una variable:
var foo = function(){ /*...*/ };
foo();
Este uso de funciones anónimas se trata con más detalle en Funciones como una variable
Suministro de una función anónima como un parámetro a otra función
Algunas funciones pueden aceptar una referencia a una función como parámetro. A veces, se hace referencia a ellas como "inyecciones de dependencia" o "devoluciones de llamada", ya que permite que la función de su llamada "devuelva la llamada" a su código, lo que le da la oportunidad de cambiar la forma en que se comporta la función llamada. Por ejemplo, la función de mapa del objeto Array le permite iterar sobre cada elemento de una matriz, luego construir una nueva matriz aplicando una función de transformación a cada elemento.
var nums = [0,1,2];
var doubledNums = nums.map( function(element){ return element * 2; } ); // [0,2,4]
Sería tedioso, descuidado e innecesario crear una función nombrada, que saturaría su alcance con una función solo necesaria en este lugar y rompería el flujo natural y la lectura de su código (un colega tendría que dejar este código para encontrar su función para entender lo que está pasando).
Devolviendo una función anónima de otra función
A veces es útil devolver una función como resultado de otra función. Por ejemplo:
var hash = getHashFunction( 'sha1' );
var hashValue = hash( 'Secret Value' );
function getHashFunction( algorithm ){
if ( algorithm === 'sha1' ) return function( value ){ /*...*/ };
else if ( algorithm === 'md5' ) return function( value ){ /*...*/ };
}
Invocando inmediatamente una función anónima
A diferencia de muchos otros idiomas, el alcance en Javascript es de nivel de función, no de nivel de bloque. (Ver Función de alcance ). En algunos casos, sin embargo, es necesario crear un nuevo alcance. Por ejemplo, es común crear un nuevo ámbito cuando se agrega código a través de una etiqueta <script>
, en lugar de permitir que los nombres de las variables se definan en el alcance global (lo que corre el riesgo de que otros scripts colisionen con sus nombres de variables). Un método común para manejar esta situación es definir una nueva función anónima e invocarla inmediatamente, ocultando de manera segura las variables dentro del alcance de la función anónima y sin hacer que su código sea accesible a terceros a través de un nombre de función filtrado. Por ejemplo:
<!-- My Script -->
<script>
function initialize(){
// foo is safely hidden within initialize, but...
var foo = '';
}
// ...my initialize function is now accessible from global scope.
// There's a risk someone could call it again, probably by accident.
initialize();
</script>
<script>
// Using an anonymous function, and then immediately
// invoking it, hides my foo variable and guarantees
// no one else can call it a second time.
(function(){
var foo = '';
}()) // <--- the parentheses invokes the function immediately
</script>
Funciones anónimas autorreferenciales
A veces es útil que una función anónima pueda referirse a sí misma. Por ejemplo, la función puede necesitar llamarse de forma recursiva o agregar propiedades a sí misma. Sin embargo, si la función es anónima, esto puede ser muy difícil ya que requiere el conocimiento de la variable a la que se ha asignado la función. Esta es la solución menos que ideal:
var foo = function(callAgain){
console.log( 'Whassup?' );
// Less then ideal... we're dependent on a variable reference...
if (callAgain === true) foo(false);
};
foo(true);
// Console Output:
// Whassup?
// Whassup?
// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
console.log('Bad.')
};
bar(true);
// Console Output:
// Whassup?
// Bad.
La intención aquí fue que la función anónima se llame a sí misma de forma recursiva, pero cuando cambia el valor de foo, terminas con un error potencialmente difícil de rastrear.
En su lugar, podemos dar a la función anónima una referencia a sí misma al darle un nombre privado, de esta manera:
var foo = function myself(callAgain){
console.log( 'Whassup?' );
// Less then ideal... we're dependent on a variable reference...
if (callAgain === true) myself(false);
};
foo(true);
// Console Output:
// Whassup?
// Whassup?
// Assign bar to the original function, and assign foo to another function.
var bar = foo;
foo = function(){
console.log('Bad.')
};
bar(true);
// Console Output:
// Whassup?
// Whassup?
Tenga en cuenta que el nombre de la función está restringido a sí mismo. El nombre no se ha filtrado en el ámbito exterior:
myself(false); // ReferenceError: myself is not defined
Esta técnica es especialmente útil cuando se trata de funciones anónimas recursivas como parámetros de devolución de llamada:
// Calculate the fibonacci value for each number in an array:
var fib = false,
result = [1,2,3,4,5,6,7,8].map(
function fib(n){
return ( n <= 2 ) ? 1 : fib( n - 1 ) + fib( n - 2 );
});
// result = [1, 1, 2, 3, 5, 8, 13, 21]
// fib = false (the anonymous function name did not overwrite our fib variable)
Expresiones de función invocadas de inmediato
A veces no desea que su función sea accesible / almacenada como una variable. Puede crear una Expresión de función invocada inmediatamente (IIFE para abreviar). Estas son esencialmente funciones anónimas autoejecutables . Tienen acceso al ámbito circundante, pero la función en sí y cualquier variable interna serán inaccesibles desde el exterior. Una cosa importante a tener en cuenta sobre el IIFE es que incluso si nombra su función, el IIFE no se eleva como lo son las funciones estándar y no puede llamarse por el nombre de la función con la que están declarados.
(function() {
alert("I've run - but can't be run again because I'm immediately invoked at runtime,
leaving behind only the result I generate");
}());
Esta es otra forma de escribir IIFE. Observe que el paréntesis de cierre antes del punto y coma se movió y se colocó justo después del soporte de cierre:
(function() {
alert("This is IIFE too.");
})();
Puede pasar fácilmente parámetros en un IIFE:
(function(message) {
alert(message);
}("Hello World!"));
Además, puede devolver valores al ámbito circundante:
var example = (function() {
return 42;
}());
console.log(example); // => 42
Si es necesario, es posible nombrar un IIFE. Aunque se ve con menos frecuencia, este patrón tiene varias ventajas, como proporcionar una referencia que se puede usar para una recursión y puede hacer que la depuración sea más sencilla a medida que el nombre se incluye en la pila de llamadas.
(function namedIIFE() {
throw error; // We can now see the error thrown in 'namedIIFE()'
}());
Aunque ajustar una función entre paréntesis es la forma más común de indicar al analizador de Javascript esperar una expresión, en lugares donde ya se espera una expresión, la notación se puede hacer más concisa:
var a = function() { return 42 }();
console.log(a) // => 42
Versión de flecha de la función invocada de inmediato:
(() => console.log("Hello!"))(); // => Hello!
Función de alcance
Cuando se define una función, se crea un ámbito .
Todo lo definido dentro de la función no es accesible por código fuera de la función. Solo el código dentro de este alcance puede ver las entidades definidas dentro del alcance.
function foo() {
var a = 'hello';
console.log(a); // => 'hello'
}
console.log(a); // reference error
Las funciones anidadas son posibles en JavaScript y se aplican las mismas reglas.
function foo() {
var a = 'hello';
function bar() {
var b = 'world';
console.log(a); // => 'hello'
console.log(b); // => 'world'
}
console.log(a); // => 'hello'
console.log(b); // reference error
}
console.log(a); // reference error
console.log(b); // reference error
Cuando JavaScript intenta resolver una referencia o variable, comienza a buscarla en el ámbito actual. Si no puede encontrar esa declaración en el alcance actual, sube un alcance para buscarla. Este proceso se repite hasta que la declaración ha sido encontrada. Si el analizador de JavaScript alcanza el alcance global y aún no puede encontrar la referencia, se generará un error de referencia.
var a = 'hello';
function foo() {
var b = 'world';
function bar() {
var c = '!!';
console.log(a); // => 'hello'
console.log(b); // => 'world'
console.log(c); // => '!!'
console.log(d); // reference error
}
}
Este comportamiento de escalada también puede significar que una referencia puede "sombrear" sobre una referencia con un nombre similar en el ámbito externo, ya que se ve primero.
var a = 'hello';
function foo() {
var a = 'world';
function bar() {
console.log(a); // => 'world'
}
}
La forma en que JavaScript resuelve el alcance también se aplica a la palabra clave const
. Declarar una variable con la palabra clave const
implica que no se le permite reasignar el valor, pero declararlo en una función creará un nuevo alcance y con eso una nueva variable.
function foo() {
const a = true;
function bar() {
const a = false; // different variable
console.log(a); // false
}
const a = false; // SyntaxError
a = false; // TypeError
console.log(a); // true
}
Sin embargo, las funciones no son los únicos bloques que crean un ámbito (si está utilizando let
o const
). let
declaraciones let
y const
tienen un alcance de la instrucción de bloque más cercana. Vea aquí para una descripción más detallada.
Encuadernación `esto` y argumentos.
Cuando toma una referencia a un método (una propiedad que es una función) en JavaScript, por lo general no recuerda el objeto al que estaba originalmente vinculado. Si el método ha de hacer referencia a ese objeto como this
no va a ser capaz de hacerlo, y decir que es probablemente causará un accidente.
Puede usar el método .bind()
en una función para crear un contenedor que incluya el valor de this
y cualquier número de argumentos principales.
var monitor = {
threshold: 5,
check: function(value) {
if (value > this.threshold) {
this.display("Value is too high!");
}
},
display(message) {
alert(message);
}
};
monitor.check(7); // The value of `this` is implied by the method call syntax.
var badCheck = monitor.check;
badCheck(15); // The value of `this` is window object and this.threshold is undefined, so value > this.threshold is false
var check = monitor.check.bind(monitor);
check(15); // This value of `this` was explicitly bound, the function works.
var check8 = monitor.check.bind(monitor, 8);
check8(); // We also bound the argument to `8` here. It can't be re-specified.
Cuando no está en modo estricto, una función usa el objeto global ( window
en el navegador) como this
, a menos que la función se llame como un método, unida o llamada con la sintaxis .call
del método.
window.x = 12;
function example() {
return this.x;
}
console.log(example()); // 12
En modo estricto, this
undefined
está undefined
por defecto.
window.x = 12;
function example() {
"use strict";
return this.x;
}
console.log(example()); // Uncaught TypeError: Cannot read property 'x' of undefined(…)
Operador de enlace
El operador de enlace de dos puntos se puede utilizar como una sintaxis abreviada para el concepto explicado anteriormente:
var log = console.log.bind(console); // long version
const log = ::console.log; // short version
foo.bar.call(foo); // long version
foo::bar(); // short version
foo.bar.call(foo, arg1, arg2, arg3); // long version
foo::bar(arg1, arg2, arg3); // short version
foo.bar.apply(foo, args); // long version
foo::bar(...args); // short version
Esta sintaxis le permite escribir normalmente, sin tener que preocuparse de enlazar this
todas partes.
Enlace de funciones de consola a variables.
var log = console.log.bind(console);
Uso:
log('one', '2', 3, [4], {5: 5});
Salida:
one 2 3 [4] Object {5: 5}
¿Por qué harías eso?
Un caso de uso puede ser cuando tiene un registrador personalizado y desea decidir en tiempo de ejecución cuál usar.
var logger = require('appLogger');
var log = logToServer ? logger.log : console.log.bind(console);
Argumentos de función, objeto "argumentos", parámetros de reposo y propagación
Las funciones pueden tomar entradas en forma de variables que se pueden usar y asignar dentro de su propio alcance. La siguiente función toma dos valores numéricos y devuelve su suma:
function addition (argument1, argument2){
return argument1 + argument2;
}
console.log(addition(2, 3)); // -> 5
objeto de arguments
El objeto de arguments
contiene todos los parámetros de la función que contienen un valor no predeterminado . También se puede utilizar incluso si los parámetros no se declaran explícitamente:
(function() { console.log(arguments) })(0,'str', [2,{3}]) // -> [0, "str", Array[2]]
Aunque al imprimir arguments
la salida se parece a una matriz, en realidad es un objeto:
(function() { console.log(typeof arguments) })(); // -> object
Parámetros del resto: function (...parm) {}
En ES6, la sintaxis de ...
cuando se usa en la declaración de los parámetros de una función transforma la variable a su derecha en un solo objeto que contiene todos los parámetros restantes proporcionados después de los declarados. Esto permite invocar la función con un número ilimitado de argumentos, que formarán parte de esta variable:
(function(a, ...b){console.log(typeof b+': '+b[0]+b[1]+b[2]) })(0,1,'2',[3],{i:4});
// -> object: 123
Parámetros de propagación: function_name(...varb);
En ES6, la sintaxis ...
también se puede usar al invocar una función colocando un objeto / variable a su derecha. Esto permite que los elementos de ese objeto se pasen a esa función como un solo objeto:
let nums = [2,42,-1];
console.log(...['a','b','c'], Math.max(...nums)); // -> a b c 42
Funciones nombradas
Las funciones pueden ser nombradas o anónimas ( funciones anónimas ):
var namedSum = function sum (a, b) { // named
return a + b;
}
var anonSum = function (a, b) { // anonymous
return a + b;
}
namedSum(1, 3);
anonSum(1, 3);
4
4
Pero sus nombres son privados a su propio alcance:
var sumTwoNumbers = function sum (a, b) {
return a + b;
}
sum(1, 3);
Error de referencia no capturado: la suma no está definida
Las funciones nombradas difieren de las funciones anónimas en múltiples escenarios:
- Cuando esté depurando, el nombre de la función aparecerá en el seguimiento de error / pila
- Las funciones nombradas se elevan mientras que las funciones anónimas no son
- Las funciones nombradas y las funciones anónimas se comportan de manera diferente al manejar la recursión
- Dependiendo de la versión de ECMAScript, las funciones nombradas y anónimas pueden tratar la propiedad de
name
función de manera diferente
Las funciones nombradas son elevadas
Cuando se utiliza una función anónima, la función solo se puede llamar después de la línea de declaración, mientras que una función con nombre se puede llamar antes de la declaración. Considerar
foo();
var foo = function () { // using an anonymous function
console.log('bar');
}
Error no detectado: foo no es una función
foo();
function foo () { // using a named function
console.log('bar');
}
bar
Funciones nombradas en un escenario recursivo
Una función recursiva se puede definir como:
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
//you could call 'say' directly,
//but this way just illustrates the example
var sayHelloTimes = say;
sayHelloTimes(2);
¡Hola!
¡Hola!
¿Qué pasa si en algún lugar de su código se redefine el enlace de la función original?
var say = function (times) {
if (times > 0) {
console.log('Hello!');
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
¡Hola!
No se detectó TypeError: say no es una función
Esto se puede resolver usando una función nombrada.
// The outer variable can even have the same name as the function
// as they are contained in different scopes
var say = function say (times) {
if (times > 0) {
console.log('Hello!');
// this time, 'say' doesn't use the outer variable
// it uses the named function
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
¡Hola!
¡Hola!
Y como ventaja adicional, la función nombrada no se puede configurar como undefined
, incluso desde dentro:
var say = function say (times) {
// this does nothing
say = undefined;
if (times > 0) {
console.log('Hello!');
// this time, 'say' doesn't use the outer variable
// it's using the named function
say(times - 1);
}
}
var sayHelloTimes = say;
say = "oops";
sayHelloTimes(2);
¡Hola!
¡Hola!
La propiedad del name
de las funciones.
Antes de ES6, las funciones nombradas tenían sus propiedades de name
establecidas a sus nombres de función, y las funciones anónimas tenían sus propiedades de name
establecidas en la cadena vacía.
var foo = function () {}
console.log(foo.name); // outputs ''
function foo () {}
console.log(foo.name); // outputs 'foo'
Post ES6, las funciones con nombre y sin nombre establecen sus propiedades de name
:
var foo = function () {}
console.log(foo.name); // outputs 'foo'
function foo () {}
console.log(foo.name); // outputs 'foo'
var foo = function bar () {}
console.log(foo.name); // outputs 'bar'
Función recursiva
Una función recursiva es simplemente una función, que se llamaría a sí misma.
function factorial (n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
La función anterior muestra un ejemplo básico de cómo realizar una función recursiva para devolver un factorial.
Otro ejemplo, sería recuperar la suma de números pares en una matriz.
function countEvenNumbers (arr) {
// Sentinel value. Recursion stops on empty array.
if (arr.length < 1) {
return 0;
}
// The shift() method removes the first element from an array
// and returns that element. This method changes the length of the array.
var value = arr.shift();
// `value % 2 === 0` tests if the number is even or odd
// If it's even we add one to the result of counting the remainder of
// the array. If it's odd, we add zero to it.
return ((value % 2 === 0) ? 1 : 0) + countEvens(arr);
}
Es importante que tales funciones realicen algún tipo de verificación del valor centinela para evitar bucles infinitos. En el primer ejemplo anterior, cuando n
es menor o igual a 1, la recursión se detiene, lo que permite que el resultado de cada llamada se devuelva a la pila de llamadas.
Zurra
Currying es la transformación de una función de n
aridad o argumentos en una secuencia de n
funciones tomando solo un argumento.
Casos de uso: cuando los valores de algunos argumentos están disponibles antes que otros, puede utilizar el curry para descomponer una función en una serie de funciones que completan el trabajo en etapas, a medida que llega cada valor. Esto puede ser útil:
- Cuando el valor de un argumento casi nunca cambia (por ejemplo, un factor de conversión), pero necesita mantener la flexibilidad de establecer ese valor (en lugar de codificarlo como una constante).
- Cuando el resultado de una función al curry es útil antes de que se hayan ejecutado las otras funciones al curry.
- Validar la llegada de las funciones en una secuencia específica.
Por ejemplo, el volumen de un prisma rectangular se puede explicar mediante una función de tres factores: longitud ( l
), ancho ( w
) y altura ( h
):
var prism = function(l, w, h) {
return l * w * h;
}
Una versión al curry de esta función se vería así:
function prism(l) {
return function(w) {
return function(h) {
return l * w * h;
}
}
}
// alternatively, with concise ECMAScript 6+ syntax:
var prism = l => w => h => l * w * h;
Puede llamar a esta secuencia de funciones con prism(2)(3)(5)
, que debería evaluar a 30.
Sin alguna maquinaria adicional (como con las bibliotecas), el curry tiene una flexibilidad sintáctica limitada en JavaScript (ES 5/6) debido a la falta de valores de marcadores de posición; por lo tanto, aunque puede usar var a = prism(2)(3)
para crear una función parcialmente aplicada , no puede usar prism()(3)(5)
.
Uso de la declaración de devolución
La declaración de retorno puede ser una forma útil de crear una salida para una función. La declaración de retorno es especialmente útil si no sabe en qué contexto se utilizará la función todavía.
//An example function that will take a string as input and return
//the first character of the string.
function firstChar (stringIn){
return stringIn.charAt(0);
}
Ahora para usar esta función, necesita ponerla en lugar de una variable en algún otro lugar de su código:
Usando el resultado de la función como un argumento para otra función:
console.log(firstChar("Hello world"));
La salida de la consola será:
> H
La declaración de retorno finaliza la función.
Si modificamos la función al principio, podemos demostrar que la declaración de retorno finaliza la función.
function firstChar (stringIn){
console.log("The first action of the first char function");
return stringIn.charAt(0);
console.log("The last action of the first char function");
}
Ejecutar esta función como tal se verá así:
console.log(firstChar("JS"));
Salida de consola:
> The first action of the first char function
> J
No imprimirá el mensaje después de la declaración de retorno, ya que la función ha finalizado.
Declaración de retorno que abarca varias líneas:
En JavaScript, normalmente puede dividir una línea de código en muchas líneas para facilitar la lectura u organización. Este es un JavaScript válido:
var
name = "bob",
age = 18;
Cuando JavaScript ve una declaración incompleta como var
, mira a la siguiente línea para completarse. Sin embargo, si comete el mismo error con la declaración de return
, no obtendrá lo que esperaba.
return
"Hi, my name is "+ name + ". " +
"I'm "+ age + " years old.";
Este código se devolverá undefined
porque el return
por sí mismo es una declaración completa en Javascript, por lo que no buscará la siguiente línea para completarla. Si necesita dividir una declaración de return
en varias líneas, coloque un valor a continuación para devolverla antes de dividirla, de esta forma.
return "Hi, my name is " + name + ". " +
"I'm " + age + " years old.";
Pasando argumentos por referencia o valor
En JavaScript todos los argumentos se pasan por valor. Cuando una función asigna un nuevo valor a una variable de argumento, ese cambio no será visible para la persona que llama:
var obj = {a: 2};
function myfunc(arg){
arg = {a: 5}; // Note the assignment is to the parameter variable itself
}
myfunc(obj);
console.log(obj.a); // 2
Sin embargo, los cambios realizados en las propiedades (anidadas) de dichos argumentos, serán visibles para la persona que llama:
var obj = {a: 2};
function myfunc(arg){
arg.a = 5; // assignment to a property of the argument
}
myfunc(obj);
console.log(obj.a); // 5
Esto puede verse como una llamada por referencia : aunque una función no puede cambiar el objeto de la persona que llama asignándole un nuevo valor, podría mutar el objeto de la persona que llama.
Como los argumentos de valor primitivo, como los números o las cadenas, son inmutables, no hay forma de que una función pueda mutarlos:
var s = 'say';
function myfunc(arg){
arg += ' hello'; // assignment to the parameter variable itself
}
myfunc(s);
console.log(s); // 'say'
Cuando una función quiere mutar un objeto pasado como argumento, pero no quiere realmente mutar el objeto del llamante, la variable argumento debe ser reasignada:
var obj = {a: 2, b: 3};
function myfunc(arg){
arg = Object.assign({}, arg); // assignment to argument variable, shallow copy
arg.a = 5;
}
myfunc(obj);
console.log(obj.a); // 2
Como una alternativa a la mutación in situ de un argumento, las funciones pueden crear un nuevo valor, basado en el argumento, y devolverlo. La persona que llama puede asignarlo, incluso a la variable original que se pasó como argumento:
var a = 2;
function myfunc(arg){
arg++;
return arg;
}
a = myfunc(a);
console.log(obj.a); // 3
Llama y solicita
Las funciones tienen dos métodos incorporados que permiten al programador suministrar argumentos y this
variable de manera diferente: call
y apply
.
Esto es útil, porque las funciones que operan en un objeto (el objeto del cual son propiedad) pueden ser reutilizadas para operar en otro objeto compatible. Además, los argumentos pueden darse en un disparo como arrays, similar al operador spread ( ...
) en ES6.
let obj = {
a: 1,
b: 2,
set: function (a, b) {
this.a = a;
this.b = b;
}
};
obj.set(3, 7); // normal syntax
obj.set.call(obj, 3, 7); // equivalent to the above
obj.set.apply(obj, [3, 7]); // equivalent to the above; note that an array is used
console.log(obj); // prints { a: 3, b: 5 }
let myObj = {};
myObj.set(5, 4); // fails; myObj has no `set` property
obj.set.call(myObj, 5, 4); // success; `this` in set() is re-routed to myObj instead of obj
obj.set.apply(myObj, [5, 4]); // same as above; note the array
console.log(myObj); // prints { a: 3, b: 5 }
ECMAScript 5 introdujo otro método llamado bind()
además de call()
y apply()
para establecer explícitamente this
valor de la función en un objeto específico.
Se comporta de manera bastante diferente a los otros dos. El primer argumento para bind()
es el valor de this
para la nueva función. Todos los demás argumentos representan parámetros con nombre que deben establecerse permanentemente en la nueva función.
function showName(label) {
console.log(label + ":" + this.name);
}
var student1 = {
name: "Ravi"
};
var student2 = {
name: "Vinod"
};
// create a function just for student1
var showNameStudent1 = showName.bind(student1);
showNameStudent1("student1"); // outputs "student1:Ravi"
// create a function just for student2
var showNameStudent2 = showName.bind(student2, "student2");
showNameStudent2(); // outputs "student2:Vinod"
// attaching a method to an object doesn't change `this` value of that method.
student2.sayName = showNameStudent1;
student2.sayName("student2"); // outputs "student2:Ravi"
Parámetros por defecto
Antes de ECMAScript 2015 (ES6), el valor predeterminado de un parámetro podría asignarse de la siguiente manera:
function printMsg(msg) {
msg = typeof msg !== 'undefined' ? // if a value was provided
msg : // then, use that value in the reassignemnt
'Default value for msg.'; // else, assign a default value
console.log(msg);
}
ES6 proporcionó una nueva sintaxis donde la condición y la reasignación descritas anteriormente ya no son necesarias:
function printMsg(msg='Default value for msg.') {
console.log(msg);
}
printMsg(); // -> "Default value for msg."
printMsg(undefined); // -> "Default value for msg."
printMsg('Now my msg in different!'); // -> "Now my msg in different!"
Esto también muestra que si falta un parámetro cuando se invoca la función, su valor se mantiene como undefined
, como puede confirmarse proporcionándolo explícitamente en el siguiente ejemplo (utilizando una función de flecha ):
let param_check = (p = 'str') => console.log(p + ' is of type: ' + typeof p);
param_check(); // -> "str is of type: string"
param_check(undefined); // -> "str is of type: string"
param_check(1); // -> "1 is of type: number"
param_check(this); // -> "[object Window] is of type: object"
Funciones / variables como valores por defecto y reutilización de parámetros.
Los valores de los parámetros predeterminados no están restringidos a números, cadenas u objetos simples. Una función también se puede establecer como el valor predeterminado callback = function(){}
:
function foo(callback = function(){ console.log('default'); }) {
callback();
}
foo(function (){
console.log('custom');
});
// custom
foo();
//default
Hay ciertas características de las operaciones que se pueden realizar a través de valores predeterminados:
- Un parámetro previamente declarado se puede reutilizar como un valor predeterminado para los valores de los próximos parámetros.
- Se permiten las operaciones en línea cuando se asigna un valor predeterminado a un parámetro.
- Las variables existentes en el mismo ámbito de la función que se está declarando se pueden usar en sus valores predeterminados.
- Se pueden invocar funciones para proporcionar su valor de retorno a un valor predeterminado.
let zero = 0;
function multiply(x) { return x * 2;}
function add(a = 1 + zero, b = a, c = b + a, d = multiply(c)) {
console.log((a + b + c), d);
}
add(1); // 4, 4
add(3); // 12, 12
add(2, 7); // 18, 18
add(1, 2, 5); // 8, 10
add(1, 2, 5, 10); // 8, 20
Reutilizando el valor de retorno de la función en el valor predeterminado de una nueva invocación:
let array = [1]; // meaningless: this will be overshadowed in the function's scope
function add(value, array = []) {
array.push(value);
return array;
}
add(5); // [5]
add(6); // [6], not [5, 6]
add(6, add(5)); // [5, 6]
arguments
valor y longitud cuando faltan parámetros en la invocación
El objeto de matriz de arguments
solo conserva los parámetros cuyos valores no son predeterminados, es decir, aquellos que se proporcionan explícitamente cuando se invoca la función:
function foo(a = 1, b = a + 1) {
console.info(arguments.length, arguments);
console.log(a,b);
}
foo(); // info: 0 >> [] | log: 1, 2
foo(4); // info: 1 >> [4] | log: 4, 5
foo(5, 6); // info: 2 >> [5, 6] | log: 5, 6
Funciones con un número desconocido de argumentos (funciones variadic)
Para crear una función que acepte un número indeterminado de argumentos, hay dos métodos dependiendo de su entorno.
Cuando se llama a una función, tiene un objeto de argumentos de tipo Array en su ámbito, que contiene todos los argumentos pasados a la función. La indexación o la iteración sobre esto dará acceso a los argumentos, por ejemplo
function logSomeThings() {
for (var i = 0; i < arguments.length; ++i) {
console.log(arguments[i]);
}
}
logSomeThings('hello', 'world');
// logs "hello"
// logs "world"
Tenga en cuenta que puede convertir arguments
a una matriz real si es necesario; ver: Convertir objetos de tipo matriz a matrices
Desde ES6, la función se puede declarar con su último parámetro utilizando el operador de resto ( ...
). Esto crea una matriz que contiene los argumentos desde ese punto en adelante.
function personLogsSomeThings(person, ...msg) {
msg.forEach(arg => {
console.log(person, 'says', arg);
});
}
personLogsSomeThings('John', 'hello', 'world');
// logs "John says hello"
// logs "John says world"
Las funciones también se pueden llamar de forma similar, la sintaxis de propagación.
const logArguments = (...args) => console.log(args)
const list = [1, 2, 3]
logArguments('a', 'b', 'c', ...list)
// output: Array [ "a", "b", "c", 1, 2, 3 ]
Esta sintaxis se puede usar para insertar un número arbitrario de argumentos en cualquier posición, y se puede usar con cualquier iterable (la apply
acepta solo objetos de tipo matriz).
const logArguments = (...args) => console.log(args)
function* generateNumbers() {
yield 6
yield 5
yield 4
}
logArguments('a', ...generateNumbers(), ...'pqr', 'b')
// output: Array [ "a", 6, 5, 4, "p", "q", "r", "b" ]
Obtener el nombre de un objeto de función
ES6 :
myFunction.name
Explicación sobre MDN . A partir de 2015 funciona en nodejs y en todos los navegadores principales, excepto IE.
ES5 :
Si tiene una referencia a la función, puede hacer:
function functionName( func )
{
// Match:
// - ^ the beginning of the string
// - function the word 'function'
// - \s+ at least some white space
// - ([\w\$]+) capture one or more valid JavaScript identifier characters
// - \( followed by an opening brace
//
var result = /^function\s+([\w\$]+)\(/.exec( func.toString() )
return result ? result[1] : ''
}
Solicitud parcial
Al igual que en el curry, la aplicación parcial se usa para reducir el número de argumentos pasados a una función. A diferencia del curry, el número no tiene que bajar uno.
Ejemplo:
Esta función ...
function multiplyThenAdd(a, b, c) {
return a * b + c;
}
... se puede usar para crear otra función que siempre se multiplicará por 2 y luego agregará 10 al valor pasado;
function reversedMultiplyThenAdd(c, b, a) {
return a * b + c;
}
function factory(b, c) {
return reversedMultiplyThenAdd.bind(null, c, b);
}
var multiplyTwoThenAddTen = factory(2, 10);
multiplyTwoThenAddTen(10); // 30
La parte "aplicación" de la aplicación parcial simplemente significa fijar los parámetros de una función.
Composición de funciones
La composición de múltiples funciones en una es una práctica común de programación funcional;
la composición es un conducto a través del cual nuestros datos transitarán y se modificarán simplemente trabajando en la función-composición (al igual que unir partes de una pista) ...
Comienzas con algunas funciones de responsabilidad única:
const capitalize = x => x.replace(/^\w/, m => m.toUpperCase());
const sign = x => x + ',\nmade with love';
y crear fácilmente una pista de transformación:
const formatText = compose(capitalize, sign);
formatText('this is an example')
//This is an example,
//made with love
NB La composición se logra a través de una función de utilidad que generalmente se llama compose
como en nuestro ejemplo.
La implementación de compose
está presente en muchas bibliotecas de utilidades de JavaScript ( lodash , rambda , etc.) pero también puede comenzar con una implementación simple como:
const compose = (...funs) =>
x =>
funs.reduce((ac, f) => f(ac), x);