javascriptの超ポリモーフィズム

javascriptにおける関数とはオブジェクトである。

では、関数の呼び出しとは何か?

答えはオブジェクトの[[call]]プロパティの呼び出しである。

内部プロパティ[[call]]は関数であるオブジェクトを実行する仕様です。
()演算子による呼び出し以外にFunctionプロトタイプオブジェクトのcall関数でも呼び出せます。
Function.prototype.callの事ですね。つまりはよくやる……

Object.prototype.toString.call(scope)

という見慣れた形式の呼び出しなどです。
例えばこれを、トップレベルスコープで……

Object.prototype.toString.call(this)

とすると文字列"[object global]"が返ります。
Globalオブジェクトの実装は必ずしもGlobalオブジェクトでなくてもよいので"[object Window]"が返ることもあるでしょう。
ちなみに必ずしもglobalというオブジェクトを表すオブジェクトが存在している必要はないので、その実行環境にはGlobalオブジェクトはないかもしれません。
では"[object global]"の"global"はなにを表しているのかと言うと、内部プロパティ[[class]]が持っている文字列です。
[[class]]プロパティはそのオブジェクト(インスタンス)の役割を表すインフォメーションです。

クラスベースのクラスではありません。そろそろ解ってきたはずですがクラスなんざありません。クラスとはオブジェクトポインタと関数テーブルの実装手法の違いでしかない幻想です。
そもそも実行環境に存在するオブジェクトはほぼ全てObjectオブジェクトのインスタンスです。
netscapeのjavascript2.0や廃止になった次期ecma-262仕様では__proto__を差し替えれたのでobjectオブジェクト以外のインスタンスを作ることができたのでその構文糖としてclass宣言がありましたがTC-39が実権を握っている以上、桃源郷やアヴァロンにたどり着くことは不可能でしょう。諦めて下さい。
それどころか、ただの構文糖なので__proto__が標準化されない限りありえません。むしろそれはマジック派の魔法使いたちの悲願でもありましょう。

大事なことなので二度言います、などと言いますから少し戻って二度言いますが、そもそも実行環境に存在するオブジェクトはほぼ全てObjectオブジェクトのインスタンスです
typeof演算子がプリミティブ以外の変数に対して文字列"object"を返すのはその為です。なのでtypeof演算子は少し不便ですその代わりに[[class]]プロパティを返す……

Object.prototype.toString.call(scope)

の方が幾ばくかフューマンフレンドリーなのでこちらがよく使われるわけです。
さて、ここからが今回の本題ですが、callの引数scopeとは何でしょうか?
ずばり、callで呼び出す関数を呼び出すスコープです。
ではスコープとは何かというとjavascriptではすべてがオブジェクトだったのでスコープとはすべてのjavascriptオブジェクトのことです。

さらにjavascriptがブロックスコープではないのはここに理由があります。ブロックと言うオブジェクトはありません。ブロックは文だからです。
なのでjavascriptでは関数スコープです。昔はWithとかClousreなんてありましたが、お亡くなりになりました。Rhinoには最近までWithオブジェクトあったんですが、お気の毒ですが消えてしまいました。

そしてこのオブジェクト(スコープ)は呼び出されるオブジェクト(関数)のスコープに入るとthisという変数に渡されます。
具体的にこのオブジェクトは何を表しているかというとその関数が属してる場所です。thisが関数自身を表さないのはその為です。


オブジェクトならスコープになり得ますので、

( function() this.a+this.b+this.c ).call({"a": 3, "b": 2, "c": 1})

このように呼び出せます。ここら辺はjavascriptの実装コードを読むとよく分かると思います。この一文はポリモーフィズム編でずとやってますね。はじめから見てきたこの文は、今につながるわけです。

では復習問題はこのへんにして、またもや駆け足で進めてしまいましょう。

var a=[]; Object.prototype.toString.call(a);

という文をFunction.prototype.call関数を使って書き換えて下さい……ハイ、待ちません。駆け足で進めます。

Function.prototype.call.bind(Object.prototype.toString).call(null, a)

Function.prototype.call(Object.prototype.toString).call(null, a)だと思った人は実に惜しいです。call関数だけでは関数Object.prototype.toStringが関数インスタンスに束縛できません。よってFunction.prototype.bindで部分適応するのが正解です。
bindはcallにObject.prototype.toStringが適応された新たな関数を返し、2つ目のcallの第一引数はそれを呼び出す側のスコープを渡します。そして第二引数、これもスコープです。2つ目のスコープは部分適応したオブジェクト(この場合はObject.prototype.toString)によって引数の扱われ方が変わりますがこちらは言わば真のthisです。

紛らわしいのでselfと呼びましょう。selfは兼ねゝ部分適応された関数そのものです。ただし、プロトタイプオブジェクトが持つ関数を渡した場合その関数に渡すべき第一引数となります。
それ以降の引数は部分適応されて作られた新たな関数へ渡す引数リストです。つまりは

Function.prototype.call.bind(Object.prototype.toString).callという呼び出し規則はObject.prototype.toString.callという呼び出し規則に変換可能です。
つまり、プロトタイプオブジェクトの持つ関数ははじめからFunction.prototype.call.bind(foo)という呼び出し規則ですからcall関数で呼び出さなければ正しく呼び出せないのです。

この要領でArrayプロトタイプオブジェクトについても見ていきましょう。

var a=[]; a.push(1);

はプロトタイプチェーンを辿ってArray.prototype.pushを呼び出します。
ならばArray.prototype.pushをFunction.prototype.callに変換すると

Function.prototype.call.bind(Array.prototype.push).call(Array.prototype, a, 1)

となります。これはArray.prototype.push.call(a,1)と同じですね。
しかし、Array、Stringプロトタイプオブジェクトの関数は今は汎用化されたのでArray.foo、String.barで呼び出せます。
それと、2つ目のcallのスコープがbindの呼び出し時に決定するならこれはbindの第二引数に持っていくことができます。ただし、スコープが適応されるタイミングが変わってくるので関数の副作用も変わる可能性があるということを念頭に置いて下さい。
それと2つ目のcallの第一引数、第二引数はundefinedに変換可能なら大体うまくいきますがこの2つがなぜそのような挙動をするかは長くなったので宿題としましょう。
自分で考えて下さい。ヒントはこれです。

Object.prototype.toString.call(this)//-> [object global]
Function.prototype.call.bind(this.escape).call(this, escape, "あ")//-> %u3042

ここまで分かればなぜjavascriptはスコープまでもオブジェクトでCall(Activation)オブジェクトといったものが存在するか、プロトタイプ・チェーンやスコープ・チェーンとは何でどういう仕組みなのか、javascriptはどのようにして実行されるのかが解ったかと思います。