Antes de empezar, primero hay que entender que son:
Las declaraciones hacen cosas. Un programa es una secuencia de declaraciones. Ej:
var foo;
var bar;
Las expresiones retornan cosas. Generalmente están del lado derecho de una asignación, o en los argumentos de una función. Ej:
2 + 2;
var foo = 'bar';
$('a[href="/"]').html();
Las formas if-else ilustran la distinción entre declaraciones y expresiones
// Declaración.
var x;
if (y >= 0) {
x = y;
} else {
x = -y;
}
// Expresión.
var x = y >= 0 ? y : -y;
function sum(x, y) {
return x + y;
}
sum(13, 10); // 23
var sum = function (x, y) {
return x + y;
};
sum(13, 10); // 23
Dos maneras de conseguir el mismo resultado. Solo que...
Las FSs son "elevadas" al inicio del bloque de código. Este comportamiento se conoce como hoisting.
// Used...
sum(21, 28); // 49
// Before being declared.
function sum(x, y) {
return x + y;
}
sum(20, 8); // ReferenceError!
var sum = function (x, y) {
return x + y;
};Según las especificaciones del lenguaje, esto es inválido:
if (x == 2) {
a = sum(4,5); // 9
function sum(param1, param2) {
return param1 + param2;
};
}
El hoisting ocurre antes de procesar el resultado del if, y los navegadores te dejan hacer el hoisting de todas maneras. Pero como la especificación del lenguaje no dice qué hacer en este caso, cada browser hace algo diferente, por lo que este es uno de esos casos extremos que se deben evitar. — Douglas Crockford.
Funciones como objectos.
Las funciones son en realidad instancias del prototipo Function.
function goToBedAt(time) {
// ...
}
typeof fn; // "function"
var fn = function (x) { return x; };
typeof fn; // "function"
Pueden ser creadas usando el constructor 'Function'.
var call = new Function('who', 'when', "console.log('Calling ', who, ' at ', when);"); // NOT RECOMMENDED.Propiedades
Métodos
Podemos pasar una función como argumento a otras funciones, de forma tal que se pueda ejecutar en esas otras funciones.
function suma(sumando1, sumando2) {
return sumando1 + sumando2;
}
var resta = function(minuendo, sustraendo) {
return minuendo - sustraendo;
};
var operacion = function(num1, num2, fn) {
return fn(num1, num2)
};
operacion(2, 2, suma); // 4
operacion(10, 8, resta); // 2
operacion(56, 7, function(dividendo, divisor) {
return dividendo / divisor;
});
Razón principal para usar funciones como unidades de ejecución es para el manejo de código asincrónico, i.e: animaciones, AJAX, eventos, etc.
$("#box").animate({"margin-top" : "20px"}, function() { $(this).css("background-color", "blue"); });app.fetchOrders(function (err, orders) { if (err) throw err; //... }); document.getElementById("box").addEventListener("click", (function(e) { alert("Hola mundo!"); });
Las cosas se pueden poner salvaje cuando se anidan callbacks. A esta situación se le llama callback hell.
var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
p_client.open(function(err, p_client) {
p_client.dropDatabase(function(err, done) {
p_client.createCollection('test_custom_key', function(err, collection) {
collection.insert({'a':1}, function(err, docs) {
collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
cursor.toArray(function(err, items) {
test.assertEquals(1, items.length);
// Let's close the db
p_client.close();
});
});
});
});
});
});
Se puede mitigar
Usando callbacks nombrados y closures*
function onFind(client) {
return function (err, cursor) {
cursor.toArray(function (err, items) {
test.assertEquals(1, items.length);
});
// Close db.
client.close();
};
}
function onInsert(client, collection) {
return function (err, doc) {
collection.find({_id: new ObjectID('abcdefgh')}, onFind(client));
};
}
function onCreateCollection(client) {
return function (err, collection) {
collection.insert({a: 1}, onInsert(client, collection));
};
}
function onDropDatabase(client) {
return function (err) {
client.createCollection('test_custom_key', onCreateCollection(client));
};
}
var p_client = new Db(/* ... */);
p_client.open(function (err, client) {
client.dropDatabase(onDropDatabase(client));
});
Más información sobre closures adelante.
Haciendo promesas
var p_client = new Db(/* ... */);
p_client
.open()
.then(function (client) {
client.dropDatabase();
})
.then(function (client) {
client.createCollection('test_custom_key');
})
.then(function (collection) {
collection.insert({a: 1});
})
.then(function (collection) {
collection.findById(new ObjectID('asdfjkl;'));
})
.then(function (item) {
test.assertEquals(item._id, new ObjectID('asdfjkl;'));
})
.catch(function (err) {
throw err;
})
.done(function () {
// Close db.
p_client.close();
});Si la función es un método de un objeto particular, "this" se refiere a dicho objeto.
var person1 = {
firstName : 'Alan',
lastName : 'Brito',
fullName: function() {
return this.firstName + ' ' + this.lastName
}
};
alert(person1.fullName()); // 'Alan Brito'
Las funciones pueden tomar un número arbitrario de argumentos. Cada argumento es asignado a su parámetro correspondiente. El resto estará disponible a través de "arguments".
function f() {
return arguments;
}
var args = f('a', 'b', 'c');
alert(args.length); // 3
alert(args[0]); // 'a'
Si no se le pasa un argumento a un parametro, su valor será undefined.
function f(a, b, c) {
console.log(a, b, c);
console.log(arguments);
}
> f('1', '2', '3');
1 2 3
[ '1', '2', '3' ]
> f('a', 'b');
a b undefined
[ 'a', 'b' ]
> f();
undefined undefined undefined
[]La palabra clave arguments se comporta similar a un arreglo. Sin embargo no tiene los métodos de arreglos.
var sum = function() {
return arguments.reduce(function(x,y) {
return x + y;
});
};
var result = sum(1,2,3); // Error!
Es posible conseguir convertir arguments a un arreglo usando Array.prototype.slice
var sum = function() { var args = [].slice.call(arguments); return args.reduce(function(x,y) { return x + y; }); };var result = sum(1,2,3); // 6</code></pre>
Se puede conseguir el efecto de parametros opcionales usando el operador ||.
function arreglo(x,y) {
x = x || 0;
y = y || 0;
return [x,y];
}
> arreglo();
[0,0]
> arreglo(2);
[2,0]
> arreglo(2,4);
[2,4]
Esto es posible dado que 'undefined' es convertido a su 'false' antes de evaluar la operación. Forzando la ejecución de la expresión a la derecha del operador.
x = false || 'It works!';
x; // 'It works!'
La aridad es un concepto matemático que se refiere al número de parámetros que recibe una función.
function identity(x) { return x }; // Arity of one.
function sum(x, y) { return x + y; } // Arity of two.
function initiateWorldDomination() { /* magic */ } // Arity of zero.
Este concepto puede ser usado junto a arguments para conseguir parámetros requeridos
function sum(x, y) {
if (arguments.length < 2) {
throw 'At least two arguments are needed.';
}
return x + y;
}El "scope" (o alcance/ámbito) de una variable es siempre la función en la que se encuentra. A diferencia de otros lenguajes de programación, el scope es por bloques.
function foo() {
var x = -3;
if (x < 0) {
var tmp = -x;
}
console.log(tmp); //3
}
"tmp" se mantuvo hasta el final de la función. En otros lenguajes influenciados por C, tmp solo duraria hasta el final del if.
La manera de como funciona el scope en C/C++/C#/Java se llama Block Scoping. Se puede conseguir este efecto usando una técnica llamada IIFE.
Immediately Invoked Function Expression (IIFE)
(function () { // block start
var tmp = '...'; // "local" variable.
})(); // block end
El paréntesis que envuelve la función evita que sea interpretada como un FS. El segundo, ejecuta inmediatamente lo devuelto por el primero. El cuerpo de la función representa un scope nuevo. Las declaraciones dentro no afectan el objeto global.
Una función se mantiene atada a su entorno. Incluso después de salir de su scope. e.g: Cuando es retornada.
function createIncrementor(start, seed) {
return function() {
return start += seed;
}
}
> var incByThree = createIncrementor(0,3)
> incByThree();
3
> incByThree();
6
> incByThree();
9Métodos de funciones
Programación funcional
Written by Efrax86
Adapted by GuyWithAfro