Function.prototype.apply()

O método apply() chama uma função com um dado valor this e arguments providos como uma array (ou um objeto parecido com um array).

Nota: A sintaxe desta função é quase idêntica a essa da call(), a diferença é que call() aceita uma lista de argumentos, enquanto apply() aceita um array de argumentos.

Sintaxe

fun.apply(thisArg, [argsArray])

Parâmetros

thisArg

O valor de this é fornecido para a chamada de fun. Note que isso talvez não seja o valor real visto pelo método: se um método é uma função em código non-strict mode , null e undefined serão substituidos com o objeto global, e os valores primitivos serão embalados.

argsArray

Um objeto parecido com um array (array-like), especificando os argumentos com os quais fun deve ser chamado, ou null ou undefined se não houverem argumentos que possam ser passados para a função. Começando com ECMAScript5 esses argumentos podem ser um objeto genérico array-like ao invés de um array. Veja abaixo a informação de compatibilidade de browsers.

Descrição

Você pode atribuir um objeto this diferente quando chamar uma função existente. this refere-se ao objeto atual, o objeto da chamada. Com apply, você pode escrever um método apenas uma vez e então herdá-lo em outro objeto, sem ter que reescrever o método para o novo objeto.

apply é muito parecido com call(), exceto pelo tipo de argumentos que ele suporta. Você pode usar um array de argumentos em vez de conjunto de parâmetros nomeados. Com apply, você pode usar um array literal, por exemplo, fun.apply(this, ['comer', 'bananas']), ou um objeto Array, por exemplo fun.apply(this, new Array('comer', 'bananas')).

Você pode também usar arguments para o parâmetro argsArray. argumentsé uma variável local de uma função. Ele pode ser utilizado para todos os argumentos não especificados do objeto chamado. Assim, você não tem que saber os argumentos do objeto chamado quando você usa o método apply. Você pode usar arguments para passar todos os argumentos para o objeto da chamada. O objeto chamado fica então responsável por manipular os argumentos.

Desde a 5a versão do ECMAScript você pode utilizar qualquer tipo de objeto que é parecido com um array (array-like), então na prática isso significa que ele vai ter uma propriedade length e propriedades inteiras no intervalor (0... length). Como um exemplo, você pode agora usar um NodeList ou um objeto personalizado como { 'length': 2, '0': 'comer', '1': 'bananas' }.

Nota: Muitos navegadores, incluindo o Chrome 14 e o Internet Explorer 9, ainda não aceitam objetos parecidos com array e irão lançar uma exceção.

Exemplos

Usando apply para cadeia de construtores

Você pode usar apply para encadear construtores em um objeto, similar ao Java. No exemplo seguinte nós iremos criar um método de Função global chamado construct, que fará você capaz de usar um objeto parecido com um array com um construtor ao invés de uma lista de argumentos

js
Function.prototype.construct = function (aArgs) {
  var oNew = Object.create(this.prototype);
  this.apply(oNew, aArgs);
  return oNew;
};

Nota: O método Object.create() usado acima é relativamente novo. Para um método alternativo utilizando closures, por favor considere a seguinte alternativa.

js
Function.prototype.construct = function (aArgs) {
  var fConstructor = this,
    fNewConstr = function () {
      fConstructor.apply(this, aArgs);
    };
  fNewConstr.prototype = fConstructor.prototype;
  return new fNewConstr();
};

Exemplo de uso:

js
function MyConstructor() {
  for (var nProp = 0; nProp < arguments.length; nProp++) {
    this["property" + nProp] = arguments[nProp];
  }
}

var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);

console.log(myInstance.property1); // logs 'Hello world!'
console.log(myInstance instanceof MyConstructor); // logs 'true'
console.log(myInstance.constructor); // logs 'MyConstructor'

Nota: Este método não nativo Function.construct não irá funcionar com alguns construtores nativos (como Date, por exemplo). Nestes casos você tem que usar o método Function.prototype.bind (por exemplo, imagine ter um array como o seguinte, para ser usado com o construtor Date: [2012, 11, 4]; Neste caso você tem que escrever algom como: new (Function.prototype.bind.apply(Date, [null].concat([2012, 11, 4])))() - de qualquer maneira essa não é a melhor forma de fazer as coisas e provavelmente não deve ser utilizado em qualquer ambiente de produção.

Usando apply e funções embutidas

A forma inteligente com que apply é utilizado permite à você usar funções nativas que de outra forma provavelmente teriam que ser escritas iterando sobre um array de valores. Aqui, como exemplo, iremos utilizar Math.max/Math.min para achar o valor máximo/mínimo value em um array.

js
/* número min/max em um array */
var numbers = [5, 6, 2, 3, 7];

/* utilizando Math.min/Math.max apply */
var max = Math.max.apply(
  null,
  numbers,
); /* Isso está prestes a ser igual a Math.max(numbers[0], ...)
                                            ou Math.max(5, 6, ...) */
var min = Math.min.apply(null, numbers);

/* vs. algoritmo simples baseado em loop */
(max = -Infinity), (min = +Infinity);

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] > max) {
    max = numbers[i];
  }
  if (numbers[i] < min) {
    min = numbers[i];
  }
}

Mas tome cuidado: ao utilizar o apply desta forma, você corre o risco de exceder o limite de argumentos do JavaScript. As consequências de fazer applying em uma função com muitos argumentos (pense em algo como dezenas de centenas de argumentos) varia de acordo com os engines (JavaScriptCore tem um limite de argumentos de 65536 hard-coded), visto que o limite (na verdade, até mesmo a natureza de qualquer comportamento de um stack excessivamente grande) não é especificado. Algumas engines irão jogar uma excessão. De uma forma mais incisiva, outras engines irão limitar de forma arbitrária o número de argumentos que poderção ser aplicados à função. (Para ilustrar esse último caso: se uma engine dessas tem um limite de quatro argumentos [obviamente, os limites atuais são significativamente maiores], isso seria como se os argumentos 5, 6, 2, 3 do exemplo anterior fossem passados ao apply, ao invés do array completo.) Se o valor do seu array puder crescer à casa das dezenas de centenas, use uma estratégia híbrida: aplique suas funções em cada bloco de array por vez:

js
function minOfArray(arr) {
  var min = Infinity;
  var QUANTUM = 32768;

  for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
    var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
    min = Math.min(submin, min);
  }

  return min;
}

var min = minOfArray([5, 6, 2, 3, 7]);

Usando apply em "monkey-patching"

Apply pode ser a melhor forma de monkey-patch uma função nativa do Firefox, ou de bibliotecas em JS. Dada uma função someobject.foo, você poderá modificar a função de uma maneira hackeresca, como por exemplo:

js
var originalfoo = someobject.foo;
someobject.foo = function () {
  // Faça coisas antes de chamar a função
  console.log(arguments);
  // Chama a função como se ela estivesse sido chamada normalmente:
  originalfoo.apply(this, arguments);
  // Rode as coisas que vem depois, aqui.
};

Esse método é especialmente útil quando você quer fazer debug de eventos, ou interagir com algo que não tem nenhuma API como os diversos eventos .on([event]... events, por exemplo aqueles utilizáveis no Devtools Inspector).

Especificações

Specification
ECMAScript Language Specification
# sec-function.prototype.apply

Compatibilidade com navegadores

BCD tables only load in the browser

Veja também