.call()
O método .call, literalmente, "chama" a sua função.
function func1(){
return 1;
}
var func2= function(){
return 2;
}
func1.call(); // 1
func2.call(); // 2
A diferença, está nos argumentos(parâmetros) passados a esta função.
Ela permite que você possa setar qual o escopo daquela função.
// criando uma variável global apenas para demonstração
window.blah= 1;
// uma função que utiliza o this
function func1 () {
console.log(this.blah);
}
// um objeto a ser usado como exemplo
var obj= {
blah: 2
};
func1(); // 1 (vindo do objeto window), escopo atual
func1.call(obj); // 2 (vindo do objecto obj)
Além disso, podemos passar parâmetros para aquela função, além do escopo atual.
function func1 (arg1, arg2) {
console.log(this.blah, arg1, arg2);
}
var obj= {
blah: "x"
};
func1.call(obj, "a", 3); // "x", "a", 3
Um macete interessante, é que por exemplo, ao utilizar o objeto arguments, ele trata-se de uma collection, e não uma Array, por tanto, não tem alguns métodos do prototype de Array, como .forEach, ou .sort.
Daí, podemos resolver isto usando:
function (){
var args= Array.prototype.slice.call(arguments);
args.forEach(...);
}
Isto funciona por que o método slice da Array basicamente pega uma "fatia" da array (o this, do ponto de vista do prototype), devolvendo em uma nova Array.
Neste caso, estamos dizendo para o método slice, que o "this" dele, é na verdade aquela collection, e ele nos devolverá uma nova Array, com a fatia de 0 até o final dela(pois não passamos nenhum outro argumento a ela).
.apply()
O método apply da função, é parecido com o call, porém, ele recebe uma lista de argumentos, na forma de uma Array, mesmo.
// igual exemplo anterior
function func1 (arg1, arg2) {
console.log(this.blah, arg1, arg2);
}
var obj= {
blah: "x"
};
// apply está enviando uma Array, com a lista de parâmetros
func1.apply(obj, ["a", 3]); // "x", "a", 3
A principal vantagem é que algumas vezes, queremos apenas repassar os argumentos recebidos em uma função, diretamente para outra, usando o próprio arguments.
function func1(){
someOtherFunc.apply(obj, Array.prototype.slice.call(arguments));
}
.bind()
O bind, funciona um pouco diferente.
Ele não executará sua função na mesma hora, como o call e o apply, ao contrário disto, ele RETORNARÁ uma nova função, a qual já estará com seu escopo definido.
// igual exemplo anterior
function func1 (arg1, arg2) {
console.log(this.blah, arg1, arg2);
}
var obj= {
blah: "x"
};
// a variável theFunction se tornará a função func1
// amarrada ao obj.
var theFunction= func1.bind(obj);
// ao chamar theFunction, o "this" dela já é o obj
theFunction("a", 123); // "x", "a", 123
Quando finalmente chamamos a função "theFunction", ela já ligada ao objeto "obj", e então, passamos neste momento os argumentos.
Uma coisa interessante que vale lembrar, é que o bind, infelizmente, tem uma performance meio pobre! Então, cuidado ao sair usando e abusando dele por aí(em especial em loops)!
Eis uma alternativa que o pessoal faz:
// igual exemplo anterior
function func1 (arg1, arg2) {
console.log(this.blah, arg1, arg2);
}
var obj= {
blah: "x"
};
// theFunction recebe, literalmente, uma função, a qual usará
// o .apply, quando chamada
var theFunction= function(){
func1.apply(obj, Array.prototype.slice.call(arguments));
};
theFunction("a", 123); // "x", "a", 123
O resultado produzido é o mesmo, mas ao contrário de utilizar o .bind, estamos utilizando o .apply, passando adiante qualquer parâmetro passado àquela função.
Finalizando
Uma outra coisa interessante, é que o "this" em uma função, pode ser qualquer coisa...
function func3() {
console.log(this);
}
func3(); // window
func3.call({ some: "prop" }); // object { some: "prop" }
func3.call("just a string"); // string "just a string"
func3.call(123); // number 123
func3.call(document.body); // object HTMLBodyElement
O que pode acabar sendo usado em situações como esta:
function calc(val1, val2){
console.log(this.max(val1, val2));
console.log(this.cos(val2));
console.log(this.min(val1, val2));
console.log(this.pow(val1, val2));
}
calc.call(Math, 2, 3); // 3, -0.9899924966004446, 2, 8
Espero que tenha sido um post útil e bem explicativo! :)
Não deixem de divulgar o post, e dar sua opinião.