arguments.callee()ってなんで使っちゃダメなの?

jsでQuineしてて、arguments.callee()なるものを知った。(それまで知らなかった)

しかし、これES5以降ではエラーになるらしい。つまり使っちゃダメ。
その理由を詳しく調べてみた。

そもそもarguments.callee()とは?

callee は arguments オブジェクトのプロパティです。関数の本体の内部で現在実行中の関数を参照するために使用します。これは関数名が不明であるとき、たとえば名前のない関数式(「無名関数」)の内部などで便利です。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/arguments/callee#Why_was_arguments.callee_removed_from_ES5_strict_mode

要するに無名関数を再帰的に呼び出したい時に、有用。

バグの温床になる!?

例えば、こんなコードがあったとしよう。!recursedtrueの時、funcAが再帰的に呼び出される。

var global = this;
console.log(this);

var funcA = function (recursed) {
    if (!recursed) return arguments.callee(true);
    if ( this != global) {
        console.log(this);
    } else {
        console.log("thisはglobalのまま");
    }
}

funcA();

funcAが引数なしで呼ばれる。thisはもちろんglobalrecursedundefinedなので、!recursedtrue
よって、funcAが再帰的に呼び出される。呼び出されたfuncAは、

  • 引数はtrue
  • レシーバがargumentsなので、thisは最初に呼び出されたarguments

②引数のrecursedtrue
この時点でのthisglobalではない(上述の通りarguments)ため、if文の真の方が実行される。

→初めの呼び出しと、再帰的に呼び出された2回目とでthisの値が変わる!

出力はこんな感じになる

2行目と7行目でthisの値が変わる

まとめ

この最初の呼び出しと再帰での呼び出しでthisが変わるという動作は思わぬバグを引き起こすため、名前付きの関数式を使うことが推奨されるようになったというのが理由のよう。

参考

arguments.callee - JavaScript | MDN
arguments.callee プロパティは現在実行中の関数を示します。

(なんか中途半端に日本語化されてますw)

タイトルとURLをコピーしました