apply() メソッドは与えられた this 参照値と、配列 (もしくは配列風のオブジェクト) の形で与えられた引数を用いて関数を呼び出します。
注: 関数の構文は call() メソッドとほぼ同じですが、根本的な違いは apply() メソッドが引数の配列を 1 つだけ受け取るのに対して、 call() メソッドは連続した引数のリストを受け取るという点です。
The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
構文
func.apply(thisArg, [argsArray])
引数
thisArg- この値は
func関数を呼び出す際に渡すthisの値です。このメソッドで指定したthisが必ず呼び出したメソッドで参照されるわけではないことに注意してください。もし呼び出したメソッドが non-strict mode 内の関数であれば、ここで渡した値がnullもしくはundefinedであった場合はグローバル・オブジェクトに置き換えられ、プリミティブ型の変数はボックス化されます。 argsArray- 1 つの配列風のオブジェクトであり、
func関数が呼ぶことになる引数を列挙したものです。関数に引数が渡されない場合はnullまたはundefinedとなります。ECMAScript 5 以降ではこれらの値は配列ではなく配列風のオブジェクトを用いる事になりました。後述のブラウザー実装状況を参照してください。
戻り値
指定した this と引数で関数を呼び出した結果が返ります。
解説
存在する関数を呼び出す時は通常と異なる this オブジェクトを渡すことができます。this はカレントオブジェクト、呼び出したオブジェクトを参照します。apply を用いることで、新たなオブジェクトのためにそのメソッドを書き直すことなく継承させることができます。
apply は、対応する引数の型を除けば call() によく似ています。引数のリストの代わりに引数の配列を用いることができます。apply は配列リテラルを引数に用いることもできます。例えば func.apply(this, ['eat', 'bananas']) のように利用でき、同様に配列オブジェクトを func.apply(this, new Array('eat', 'bananas')) のように利用できます。
argsArray で arguments を利用することもできます。arguments は関数内のローカル変数です。これは、呼び出されたオブジェクトの不特定の引数すべてに対して用いることができます。つまり、 apply メソッドを使うにあたって呼び出されたオブジェクトの引数について知る必要がないのです。arguments を利用することで、引数のすべてを呼び出されたオブジェクトに渡すことができます。引数の扱いは、呼び出されたオブジェクトが受け持ちます。
ECMAScript 第5版以降では、配列風のオブジェクトも使えます。具体的には、length プロパティとその範囲 (0 から length-1 まで) の整数の名称のプロパティを持った、あらゆる種類のオブジェクトの利用を認めています。例えば、NodeList や { 'length': 2, '0': 'eat', '1': 'bananas' } のような独自のオブジェクトを利用できます。
Chrome 14 や Internet Explorer 9 などのブラウザでは、配列風オブジェクトを扱えずに例外がスローされます。
例
apply で配列を別の配列に追加する
push で 1 つの要素を配列に追加できます。そして、push は可変長引数に対応しているので、複数の要素を一度に追加することもできます。しかし push に配列を渡すと、配列の要素ごとにではなく配列全体を 1 つの要素として追加してしまいます。配列の中に配列が入るだけなのです。それを望まない場合はどうすればいいのでしょう?concat ならば望みの結果を得られます。しかし、既存の配列に追加するのではなく、新しい配列を生成して返します。既存の配列に追加したいのに... では、どうすれば?ループ文を書きますか?おことわりですよね?
apply が救ってくれます!
var array = ['a', 'b']; var elements = [0, 1, 2]; array.push.apply(array, elements); console.info(array); // ["a", "b", 0, 1, 2]
apply をビルトイン関数と共に利用する
apply を賢く使うと、本来なら配列のためにループ文を書かなくてはならないような処理に対して、ビルトイン関数をそのまま使えるようになります。下記の例では、配列の最大値・最小値を求めるために Math.max/Math.min を使っています。
// 最小値・最大値を求めたい配列
var numbers = [5, 6, 2, 3, 7];
// Math.min/Math.max と apply を使う
var max = Math.max.apply(null, numbers);
// これは右と同じ Math.max(numbers[0], numbers[1], ...)
// または Math.max(5, 6, ...)
var min = Math.min.apply(null, numbers);
// 対して、ループ文を使うとこうなる
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];
}
}
しかし注意してください。この方法で apply を使う場合、JavaScript エンジンの引数の長さ上限を超えてしまう危険があります。多すぎる (おおよそ数万個以上だと思って下さい) 引数を与えた結果は、その上限が特に決まっていない (本当に任意の巨大なデータのかたまりに対してさえ) ためエンジンによって (JavaScriptCore ライブラリでは引数の上限は65536であるとハードコーディングされています) 異なります。例外を投げるエンジンも存在します。さらに酷い場合では、関数へ実際に渡す引数の数を勝手に制限するものもあります。後者について詳しく解説しますと、そのエンジンの引数の上限が 4 つの場合 (実際の上限値は当然もっと上です)、上の例では、完全な配列でなく 5, 6, 2, 3 が apply へ渡されたかのような動作をします。
もし実装しているコードで利用する配列の変数の数が数万を超えそうなときは、以下に示すように一度に apply に渡す配列を分割して利用する方法を併用すべきでしょう。
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]);
apply を利用したコンストラクタチェーン
apply を利用して、Java のように constructors の連結を行なうことができます。以下に示す例ではグローバルな construct という名称の Function オブジェクトを作成し、引数のリストの代わりに配列風オブジェクトをコンストラクタと共に利用できるようになります。
Function.prototype.construct = function(aArgs) {
var oNew = Object.create(this.prototype);
this.apply(oNew, aArgs);
return oNew;
};
注: 上記で使用している Object.create() メソッドは比較的新しいです。代わりの方法として、以下の例を検討してください。
Object.__proto__ を利用:
Function.prototype.construct = function (aArgs) {
var oNew = {};
oNew.__proto__ = this.prototype;
this.apply(oNew, aArgs);
return oNew;
};
クロージャを利用:
Function.prototype.construct = function(aArgs) {
var fConstructor = this, fNewConstr = function() {
fConstructor.apply(this, aArgs);
};
fNewConstr.prototype = fConstructor.prototype;
return new fNewConstr();
};
Function コンストラクタを利用:
Function.prototype.construct = function (aArgs) {
var fNewConstr = new Function("");
fNewConstr.prototype = this.prototype;
var oNew = new fNewConstr();
this.apply(oNew, aArgs);
return oNew;
};
使用例:
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); // Hello world!
console.log(myInstance instanceof MyConstructor); // true
console.log(myInstance.constructor); // MyConstructor
注: この非ネイティブな Function.construct メソッドはいくつかのネイティブ実装されたコンストラクタ (例えば Date のような物) と併用できません。このようなケースにおいては Function.bind メソッドを利用する必要があります。 (例えば [2012, 11, 4] のような配列を利用する事を想定してください。この場合では次のように記述する必要が有ります。new (Function.prototype.bind.apply(Date, [null].concat([2012, 11, 4])))() - いずれにせよこれは最適な選択肢とは言えず、実用上はいかなる状況でも用いるべきではないでしょう)
仕様
| 仕様書 | 策定状況 | コメント |
|---|---|---|
| ECMAScript 3rd Edition (ECMA-262) | 標準 | 初期定義。JavaScript 1.3 で実装 |
| ECMAScript 5.1 (ECMA-262) Function.prototype.apply の定義 |
標準 | |
| ECMAScript 2015 (6th Edition, ECMA-262) Function.prototype.apply の定義 |
標準 | |
| ECMAScript Latest Draft (ECMA-262) Function.prototype.apply の定義 |
ドラフト |
ブラウザー実装状況
| デスクトップ | モバイル | サーバー | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
apply | Chrome 完全対応 あり | Edge 完全対応 12 | Firefox 完全対応 1 | IE 完全対応 あり | Opera 完全対応 あり | Safari 完全対応 あり | WebView Android 完全対応 あり | Chrome Android 完全対応 あり | Firefox Android 完全対応 4 | Opera Android 完全対応 あり | Safari iOS 完全対応 あり | Samsung Internet Android 完全対応 あり | nodejs 完全対応 あり |
ES 5.1: generic array-like object as arguments | Chrome 完全対応 あり | Edge ? | Firefox 完全対応 4 | IE ? | Opera 完全対応 あり | Safari ? | WebView Android 完全対応 あり | Chrome Android 完全対応 あり | Firefox Android 完全対応 4 | Opera Android ? | Safari iOS ? | Samsung Internet Android 完全対応 あり | nodejs ? |
凡例
- 完全対応
- 完全対応
- 実装状況不明
- 実装状況不明