ページ

ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

2012-06-01

拾い読み:Android と iPhone のブラウザー戦争


developerWorks Japanから、Android と iPhone のブラウザー戦争

ちょっとばかり面白そうなネタです。ブラウザ戦争を扱っているわけではなく、両方で使えるWebアプリの作成方法の解説記事です。


WebKitならHTML5がいけるからWebアプリ書くのが楽だよという記事(すごい大雑把ですが)。その中でモバイルに対応するときの注意点が書かれています。
  1. 専用ページを作る必要は無い
  2. マウスポインタと指のサイズの違いに注意(まあアクション点が指で識別できるように、ということですね)
  3. ホバー・スタイルは使わない。まあ、指ではホバーできません。
  4. マウス関係のイベントの注意。マウスを押したままで範囲指定とかできません。
画面サイズの違い(ピクセル数的にあるいは実サイズ的に)については viewport メタタグ推奨。そしてユーザがモバイル版、デスクトップ版を切り替えることができるようにすること、といったところですね。

そして例題としてネットワーク監視アプリを作成。Web+JSで監視アプリを書いてみて Safari、Androidで使うとこうなる、という辺りを解説。まあコード解説ですな。


第1回のアプリのチューンアップといった感じでしょうか。HTML5のローカルデータベースを使っているみたいなので、良いサンプルになりそうです。ただ、そこそこに古い記事なので用語は現在のものと微妙に違っているみたいです。

なるほど、最後のところで書かれていましたが、第1回のアプリの機能拡張としてローカルストレージ、Ajaxの使い方を示す、といった方向性のものだったのですな。読みでのある記事でした。

2010-12-13

拾い読み:控えめな JavaScript と Ajax を作成するためのガイド



現状でもこういったルールに縛られるべきなのでしょうか。理想をいえばJSオフでも動作するページが欲しいのは確かです。特に未だに酷いJSコード埋め込まれたページもありますから、JSオフでブラウザを使うこともありますが、今時の基本ではJSオンだと思うのです。JSオフ対策って、現時点ではどこまでやるべきなのですかね。更にAjaxでも、となると難しいでしょう。装飾レベルでJSを使っているケースならともかく、GoogleMapやDocsみたいなページでJSオフ対策するのはナンセンスになります。

JSオフ対策が必要な場合と、JSオフブラウザを見捨てる境目をどのあたりに置くべきか、考えさられるものがあります。

ニュース拾い読み:Chrome、再び最速ブラウザの座へ


新スクリプトエンジンの話。Win32用最新開発版には搭載済みとのこと。他の点はともかく、JSの実行性能に関してはトップ性能でしょう。



こちらが、chromeに実装されたJSの高速化技術 Crankshaftの解説。JSベンチマークに詳しくないと、どのあたりが高速化されたのかよくわかりません。ですが、今やJSの処理性能がブラウザの性能を決めるような状況になってきています(Googleのサービス使うと特に)ので、こういう高速化はありがたいことです。

でも、これってGoogleによるマッチポンプかも知れません。高速JSエンジンが無いとGoogleサービス使ってもらい難くなるわけですからね。

2010-12-12

JavaScriptの勉強:Ajax時代のJavaScriptプログラミング再入門


古い(2007/06~2007/09)のが難点。ツールガイド系はちょっと古くなっただけで無意味になるものが多いので。


読みどころはAptana Studioの紹介あたりでしょうか。ですが、検索かけてみると、Aptanaについての新しめの情報は見つかりません(2008頃までですね)。廃れてしまっているのでしょうか。


Functionコンストラクタによる定義は面白いですね。これは知りませんでした。後半に書かれていますが、関数コンストラクタによる定義と無名関数定義の違い、使い分け、あたりは興味のあるところです。後で出てくるようなのでその時に。高階関数では無名関数の出番で関数コンストラクタは使えない、と無名関数は普通に使うので関数コンストラクタが便利になるポイントで出てこないと有難味がありません。そのあたりはスコープに絡んだ話になるようです。


ローカルスコープ実現のためにこのような方法が紹介されていますが、こういうのはJS使いにとっては常識なのでしょうかね。

(function() {
  for (var i  =0; i < 10; i++) {
    // ブロック内の処理
  }
})();

以前に読んだJavaScript再入門でクロージャのメモリリーク防止のための手法として同じように無名関数内で処理してしまう手法が出ていました。ある意味都合のいい(外部に一切影響を残さない)コードですが、あまり多用されるとコードが見難くなりそうです。

無名関数と同様に匿名オブジェクト(with (匿名オブジェクト){})でも疑似ブロック・スコープが構築できるのですね。確かにスコープを作るのであればこちらの方が見た目が良いです。

で、出てきました「関数リテラルとFunctionコンストラクタにおけるスコープの違い」。関数リテラル(無名関数)を定義する時はローカルな環境を取り込むのに対して、Functionコンストラクタはグローバルオブジェクト決め打ちになっているのですね。

クロージャの仕組みを理解する 単純に、内部状態を持った関数(オブジェクトではなく)を作るにはこうすればいいのですね。JavaScript再紹介のリンク先では内部変数、内部関数でクロージャを作って private 変数を実現していましたが、単一関数での内部変数を用意するならこちらの方が簡単です。


このあたりは、以前にJavaScript再入門を読んだ時にしっかりと解説されていた部分です。最初からこちらの解説を読んでも良かったかもしれません。解説対象がほぼ同じ、レベルもほぼおなじでしょう。

拾い読み:いまさら聞けないリッチクライアント技術

@ITで 「いまさら聞けないリッチクライアント技術」

少々古い記事(2007/06~2009/03)ですが、これだけまとめられたものは滅多に見ません(個別のものはいくらでもありますがね)。軽い解説なので、簡単に読めました。ひと通り目を通しておいて損は無いでしょう。RIA技術は範囲が広いので、すべてに通じている人はまずいないでしょうから。

まじめに勉強するとなると、それぞれの回に関連情報へのリンクが載っていますので、そちらで、ということでしょう。ただ、RIA技術も進歩が速いので、今となっては時代に合わなくなっているものも含まれているので注意が必要でしょう(ブラウザの回でChrome載っていないのです)。


一応、扱い的にはまだ「連載中」らしいのですが、実質的には連載終了でしょうね。RIA技術の場合、時間経過とともに記事の価値が失われやすいのが残念なところです。

JavaScriptの勉強:世界で最も誤解されたプログラミング言語

JavaScript再紹介からのリンクで。

誤解の元になった点、および現在進行中のまずいところのリストになっています。2001年段階での情報ですね。いまでは変わって来ている点もあるかと思います。やはりGoogleMapも出現、JSの歴史的には大事件だったと思います。

その名前

Javaなんて名乗っていながらJavaとは全く別物ですからね。最初にこんな名前を付けた奴は切腹ものでしょう。

Cの皮を被ったLisp

これは言い得て妙、でしょう。実行制御系はCのような言語系統ですが、オブジェク管理、特にメソッド管理のあたりはLisp風ですね。他の部分は難なく理解できるのですが。メソッド管理のあたりが実に難解です。

固定概念

確かにWEBブラウザ以外でJavaScript使おうという人は滅多にいませんでしたからね。しかしnodejsみたいなもの出てきていますので、このあたりも様変わりしてきていると思います。

定まらぬ目標

この筆者、かなりECMA委員会をくさしていますね。言語仕様の変遷までは追っていませんのでなんとも言いがたいのですが、委員会ベースの決定というのは、しかも自分らが実装するのではなければ、無責任になんでも取り込みたがるものではないでしょうか。

設計ミス

この点についてもECMA委員会を叩いていますね(彼らは新しい問題を作る方により興味があるのでしょう)。やり過ぎな感じの自動変換は全く同意します。他の部分については困るほど使い込んでいませんので、なんともいえませんが、エラーを出し易いというところは同意です。ですが外部ツールとしてリントがあるのですか。そういうのと組み合わせればなんとかあるかも知れません。が、このページからの紹介リンクはリンク切れになっています。今は専用サイト持っているのですね。ですが、DOMなんかへの参照があってもきちんと検証できるものなのでしょうか?

まずい実装

全く初期の実装はひどいものでした。私なんかは相当長い間、ブラウザのJSはオフにしたままでいました。下手にJS走らせるとブラウザが固まってしまうことが多かったので(普通と違う環境でしたから)。

ひどい書籍

まあ、どうみてカスな本が大量に出まわっていましたね。筆者おすすめは2冊だけ、ですが、英語版の改定版を読んだ人の話では「Dynamic HTML」の方は消えてオライリーの「JavaScript」だけになっているそうです。Dynamic HTML本の方はDOM周りの仕様改訂について行けなくなったのでしょう。書籍だけでなくWEBページのJSの解説もなかなかいいものは見つかりませんね。こうすれば動くよ、レベルの超入門レベルか、これすげーだろう、といった変態コード自慢の両極端に偏っています。

標準以下の標準

筆者本当にECMA委員会嫌っていますね。「ECMAとTC39委員会は、深く反省するべき」だそうで。ひょっとして公式仕様書って、ECMA委員会委員会を恨みたくなるようなものなのでしょうかね。

アマチュア

これも変わってきていそうですが。JSの再評価に繋がったGoogleMap出現が2005年ですから、それ以前の記事としてはアマチュアばっかり、という感じだったのでしょう。今ではプロユースになってきていると思います。

オブジェクト指向

まあ、何を以てオブジェクト指向というかは人それぞれですからね。JSも本格的に使われるようになってからはオブジェクト指向言語と認められるようになっています。BUZZWORD的にいうとプロトタイプベースのオブジェクト指向、だそうです。以下翻訳ベースで。

一般にオブジェクトシステムの構成方式には、継承(inheritance (is-a) )、または、集約(aggregation (has-a))があります。JSでは両方可能なのですが(知らなかった)に、JSの動的な特性から明らかなように集約が優先して使われています(ほぼこちらしか見受けません)。

JSがちゃんとしたOO言語ではない、という批判があります。ひとつは、JSには情報隠蔽の仕組みが用意されていない、という点についえの批判です。通常のJSでのオブジェクト構成では、すべてのメンバがパブリックになってしまい、プライベート変数、メソッドを持たせることができない、というものです(実際、普通に見かける解説では見たことがありません)。しかし、実際にはJSでもプライベート変数、メソッドを持たせることができます[こちらのページで解説]。

補足:
上のリンク先ページでは、JSのクロージャ機能を使うことによってプライベート変数、メソッドを持たせる手法が解説されています。確かに可能にはなっていますが、なんか成り行きで使用可能になっているように見受けられます。他のOO言語のようなきちんと設計されたものとはレベルが違っています。

もうひとつの批判はちゃんとした継承機能を持っていないという点についてです。しかし、実際にはJSには古典的な継承だけでなく、他のコード再利用パターンも用意されています。

補足:
こちらのリンク先では、prototype チェインを使った継承(単一、多重)が解説されています。これも、なるほど可能になっていますが、いかにも無理矢理感ただよう実装です。可能であるからOO言語、っていうのは相当に無理があると思いますがね。このレベルでOOサポートと称するのであれば、Cだって、ASMだってOO言語ということになってしまいます。ただ、リンク先に出ているサポート用の4関数、inherits、method、uber、swiss、は結構色々なシチュエーション(それこそ本来のOO機能が要求された時)で使いでがありそうです。

JavaScriptの勉強:A re-introduction to JavaScript

これはいいですね。一貫して記述されています。まずはこれをじっくり読むこととしましょう。


基本データ型と制御構造については、既存の、あるいは他の言語とほぼ同じといっていいでしょう。全くの初心者以外はそのあたりは読み飛ばしで充分です。他の言語と大きく違ってくるのは、オブジェクトの扱いです。ということで、この文書でも「オブジェクト」以降をじっくり読むことにします。

オブジェクト

JSでは基本型以外はすべてオブジェクトとして扱われるそうです。そして、オブジェクトは名前付きの連想配列になっていると。マシン性能の向上によって連想配列の処理が高速化されたことで、連想配列ベースでもそれなりの性能が得られるようになったからでしょうね。昔の貧弱なマシンでは連想配列は便利だけど実用性に乏しいと考えられていましたから。他言語のハッシュ系と比較していますが、連想配列が本来のデータ構造で、その効率的な実装としてハッシュマップが採用されている、というのが正しい言い方でしょう。機能としての連想配列はハッシュ以外でも実装できますから。連想配列はキーバリューペアの集合として構成されます。JSではキーが文字列、バリューは任意のもの(基本型、あるいはオブジェクト)です。バリューとしてオブジェクトを持つことができるので、任意の複雑なデータ構造(あるいはとんでもないデータ構造)が構成できるようになっています。

オブジェクトは「new」演算子にとって、あるいはオブジェクトリテラル表現({}でくくります)によって、(動的に)生成されます。リテラル表現は初期のJSにはなかったそうですが。他のスクリプト言語でもリテラル表現を持つものと持たないものがありますが、リテラル表現が無いとコードがすごく読み難くなります。結構重要なポイントでしょう。今のperlはどうですかね。昔のperlにはリテラル表現が無かったので、そのためだけにPython使ったことがありましたが。

var o = new Object();  new 演算子で生成
var o = {} ;                  オブジェクトリテラルで生成

オブジェクトのプロパティなる言葉が唐突に(説明なしで)出現しています。まあ、これぐらいは誰でも判るはずなのでいいのかも知れませんが、文書の組み立てとして引っ掛かる点です。実態としてはオブジェクト=名前付き連想配列で、プロパティ=連想配列の要素、ですね。プロパティへの参照方法、"."参照と、インデックス参照”[]"がでてきます。プロパティと言われると定義していないのに、いきなり参照可能なところが気持ち悪いのですが、連想配列であれば、そのあたりは勝手に使用できるので納得できます。

  o.name = "Name";     "."演算子による参照
  var n = o.name;

  o["name"] = "Name";   インデックス参照
  var n = o["name"];

ただ、こういう定義なしで勝手に使える機能というのは便利なようでありながら、大規模コードを書く場合にはエラーの発生源になります。このためコンベンショナルな言語では、あえて、このような機能は使えないようにしています。例えば上でキー文字列を name、namae と間違えたとしましょう。コンベンショナルな言語では言語処理系によって間違いが検出されますが、連想配列ではどちらも正しいので、そのまま動作してしまいます(が結果は変になります)。大規模コードでこのような間違いを探すのはとても大変です(perlでこの手のミスに何度泣かされたことか)。

配列

連想配列ベースのくせに専用の「配列」があるのですね。少々気持ち悪いのですが。配列も(連想配列ベースの)オブジェクトであるなら色々ないたずらができそうですが、どうなるのでしょう。例えば a.length に変な値を入れて見るとか、変なインデックス(数字以外)で参照して見るとか。試してみました。a.length 勝手に変更できてしまいます。小さい値にすると配列の後ろのデータが失われます。大きな値にした場合にはその時には特に影響ないようですが、後で、例えば a.push("c") すると、勝手に変更した length のところに設定されます。勝手なインデックスを指定した場合には、オブジェクト(連想配列)にはきちんと記録されますが、配列を評価した場合には本来の配列要素しか表示されません。まあ、いろいろと気持ちの悪い動作になりますね。

注意:
JS Console にJSコードをペーストして実行させると(JSコンソールがマルチライン入力できないので)、本来の行単位とは微妙に異なった順序で実行されてしまうようです。例えば、以下の文をペーストして実行させると、ログ出力(イタリック部分)では配列は最終的な状態で示されます。

var a = ["A","B"];
console.log(a.length, a);
a.push("C");
console.log(a.length, a);
a.push("D");
console.log(a.length, a);
2 ["A", "B", "C", "D"]
3 ["A", "B", "C", "D"]
4 ["A", "B", "C", "D"]

しかしこれを分割して実行させると、次のように正しい結果になります。

var a = ["A","B"];
console.log(a.length, a);
2 ["A", "B"]
a.push("C");
console.log(a.length, a);
3 ["A", "B", "C"]
a.push("D");
console.log(a.length, a);
4 ["A", "B", "C", "D"]

JS Console での行入力評価のメカニズムの問題だと思いますが、気持ち悪いですし、何をやっているのか判らなくなりますので、注意が必要でしょう。

関数

単純に関数だけを取り上げた場合には普通の言語と変わりません。無名関数もいまやどこでも使われる技術になってきていますので、珍しいものでもないでしょう。多少違いがあるのは、パラメタについてのチェックが全く行われない点と、関数呼び出しに関する情報が関数内部からアクセス可能になっている点ぐらいでしょうか。呼び出し情報は「arguments」という変数(と書かれていますがオブジェクトと呼びたいですね)に格納されていて、呼び出された関数そのもの(無名関数の再帰呼出で必須)とパラメタリスト(パラメタ配列)が含まれています。色々なシチュエーションで使えます。

カスタムオブジェクト

このあたり、スクリプト言語派の悪い癖、こんなこともできるんだぜい!、が出ていますね。カスタムオブジェクトの構成方法、使い方とは関係のないことで異様に盛り上がっています。スクリプト屋ってどうしてこういうことに凝りたがるのでしょうね。というわけでここは無視。自前で調べていったところをまとめておきます。

new と this による結合

上のオブジェクトの生成方法のところで、new によって生成する方法と、オブジェクトリテラルで生成する方法とが説明されていました。非常に重要なくせにそこではスルーされていたのですが、newによってオブジェクトを生成する場合には、その対象(newの右側)は関数でなければなりません。実際サンプルでも

var obj = new Object();

と、(判りにくいですが)Object関数(ちゃんと()が付いています)に対して適用しています。普通の言語では new の右はクラス名ですが、JSではこれが関数(名前付き)になります。つまり他の言語でのクラス定義に相当することをJSでは関数で行ないます。すごく気持ち悪いですが。

var obj = new Func();

newによって新しいオブジェクトが生成されます。初期状態で生成されたオブジェクトは空(厳密には Object のクローン)になっています。新たに生成されたオブジェクトは、関数内では this によって参照されます。関数内で this 経由で新規に生成されたオブジェクトの連想配列にデータや関数を追加していくことができます。thisを使わない限り生成されたオブジェクトは Object() のクローンに過ぎないので、Objectの機能しか使えません。

new の対象となる関数内で this による新規生成されるオブジェクトとのバインディングが行われる場合、そのような関数をコンストラクタ関数と呼びます。実際には空で作成されるオブジェクトへの初期設定を行なう関数になっているわけですね。new の対象になる関数に this による参照(バインディング)が存在していなければ、生成されるオブジェクトは空のままです。構文的には同じでも無意味な文になります。

つまり、

var obj = new Func()

は、概念的には以下の処理と同じことをやっていると考えることができます(後述のように他のこともやるようですが)。

var obj = new Object();
Func(this = obj)

このように考えると、個人的にですが、JSの関数、オブジェクトの関係に、やっと納得することができました。オブジェクトの初期化を行なうので、new の右には関数が必要、という発想なのでしょう。

Prototype

コンストラクタ関数ですが、いつも空の Object クローンから組み立てていくのはナンセンスです。普通のオブジェクト指向言語でいうところの継承メカニズムも必要になります。それを支えるものが prototype オブジェクトです(単なる連想配列と理解した方が判りやすいですね)。"prototype"連想配列は生成元のコンストラクタ関数に結合されているイメージで、その結果、同じコンストラクタ関数から生成(new)されたオブジェクトは同じ(コンストラクタ関数に結合された)prototype連想配列を共有します。つまり、コンストラクタ関数の prototype 連想配列に記載されたキーバリューペアは、そのコンストラクタから生成されたすべてのオブジェクトで共通して使用することができる、ということです。

"protptype"連想配列に対しては、特殊なルックアップが行なわれ、this. による参照が(prototypeを明示しなくても)及びます。オブジェクトインスタンスに目的のキー名が見つからなかった場合には、ルックアップはコンストラクタ関数の prototype 連想配列に対して行なわれ、さらにコンストラクタ関数の元になった更に上位のコンストラクタ関数に、最終的には Object コンストラクタにまで及びます。そのため、特に定義していなくてもObjectコンストラクタで定義されている toString メソッドが利用可能になるわけです。

オブジェクト(クラス)の継承は prototype 連想配列のルックアップの連鎖によって行なわれます。問題というか便利というか、prototype 連想配列のマップはプログラムから変更できてしまいます。このため都合に合わせて上位クラスの振る舞いを変えてしmじゃうことができるのですが、気を付けないと思わぬ副作用を生むことになります。まあ、直接の親以外は変にいじらないのが正解でしょう。スクリプト派の意見ではこれは非常に便利な機能、ということですが、 strictly typed 派としては危険極まりない機能、といえます。

気になる点:
解説に出ているコードでは、コンストラクタ関数の外部で prototype に関数を設定(定義)しています。ですが、これは非常に気持の悪いコードです(クラス定義は同じところで行なわれるべきです)。やり方としては、コンストラクタの prototype にメソッド定義があるかどうかを調べ、なければ追加する、という方法が考えられますが(下記のコード例)、これとてあまり綺麗とはいえません。どのような方法がJS的に「正しい」方法なのか知りたいところです。

function PersonName(pname, fname) {
  this.familyName = fname ;
  this.personalName = pname ;
  if (! PersonName.prototype.getWesternName) {
     PersonName.prototype.getWesternName = function() {
       return (this.personalName + " " + this.familyName) ;
     }
     PersonName.prototype.getEasternName = function() {
       return (this.familyName + " " + this.personalName) ;
     }
  }
}

内部関数

関数名がひどい(もっとましな例が必要)ですが、意味的には判ります。関数内に更に関数を定義した場合、内部の関数は上位関数の変数にアクセスできる(ので複数の内部関数で来データを共有することができる)のですが、外部からはその変数にも内部関数にもアクセスできない、という隠蔽メカニズムが働きます。

しかし、本当に大規模プログラムを書くのであれば、ここで例としてあげられたメカニズムだけでは充分ではありません。オブジェクト内の変数、関数に対する隠蔽メカニズムが必要です。でないと、上位関数から戻った時点で内部変数は失われてしまいます。オブジェクトが生存している間維持されながら、外部からはアクセス出来ない内部変数、内部関数がサポートされなければなりません。今までに出てきた方法では、オブジェクトのインスタンス変数は this によって(新規生成される)オブジェクトに結び付けられます。その結果、それらの変数、関数は外部からアクセス可能になってしまいます。

このあたりは、このページからリンクされていた「世界で最も誤解されたプログラミング言語」の更にその先にリンクされていた(英語の)解説ページ、Private Members in JavaScript、Classical Inheritance in JavaScriptで実現する手法が説明されています。可能なのはわかりますが、使いやすいとは言いがたい代物です。なお、これらの手法では後述のクロージャを利用していますので、クロージャの解説を理解してから読むべきです。

クロージャ(closure)

クロージャも使える言語が増えてきています。ここでは実際のコードでクロージャの実例を示しています。

function makeAdder(a) {
    return function(b) {
        return a + b;
    }
}
x = makeAdder(5);
y = makeAdder(20);
x(6)
11
y(7)
27

関数 makeAdderは、無名関数を返す(従って返り値は加算関数)のですが、そこで、外側の関数の変数 a への参照が含まれています。xが生成された時にはその値は5、yが生成された時にはその値は 20、で生成された関数にはその時の値が保持されているわけです。このような、実行時の環境(この場合には外側の関数の変数aの値)を取り込んで生成された関数をクロージャと呼びます。

JSでは関数が実行されるときには、その関数が参照する外部データのコピーを保持するオブジェクト 'scope' が関数とセットで生成されます。上の例では x が生成されたときには値5の変数aを持ったスコープが、yが生成されたときには値20の変数aを持ったスコープが生成されています。スコープは生成された関数への参照(x、y)が存在する間保持され、参照が失われるとガベージとなって(いつか)回収されます。


function Counter(init) {
  var ival = init ;
  function privdec() {
    ival = (ival-1) > 0 ? (ival-1) : 0 ;
    return ival ;
  }
  function privinc() {
    ival += 1 ;
    return ival ;
  }
  function privshow() {
    return ival ;
  }
  this.inc = function() { return privinc() } ;
  this.dec = function() { return privdec() } ;
  this.show = function() { return privshow() } ;
}
var c = new Counter(2);
undefined
c.show()
2
c.inc()
3
c.inc()
4
c.dec()
3
c.ival
undefined

上のコードではコンストラクタ関数の内部変数 ival を内部関数 privinc,privdec,privshow から参照させることで、生成されたオブジェクト c のクロージャに含めていて、結果としてオブジェクトのプライベート変数(最後の行が示すように外部からはアクセス不可)を実現しています。

メモリリーク

クロージャの副作用としてメモリリークが発生しやすくなります。function外部への参照が含まていると、それらの参照先を取り込んだクロージャが生成され、目に見えない形で格納先の変数、オブジェクトに結び付けられます。クロージャは格納先の変数、オブジェクトが無効化されたなら(参照が無くなったなら)ガベージコレクタによって回収されます。が、明記されないだけあって、参照が残ってしまうこともありますし、相互に参照し合う循環参照が起きると、そのようなオブジェクトは(プロセスが終了しない限り)永久に残ってしまいます。このページでは以下のような、クロージャによる循環参照の発生例が載っていました。いかにもありがちなコードです。

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
}

内側の無名関数は、外側の addHanlder関数の el 内部変数に格納されるため、クロージャが生成され、el.onclick に結び付けられます。一方生成されたクロージャには el.onclick が含まれています。このため、変数 el と無名関数のクロージャは相互に参照し合う関係になり、結果、これらのオブジェクト、クロージャで使用されたメモリは回収されなくなります。

この問題の解決策ですが、このページで解説されている方法はふたう。ひとつはこの手のGC依存言語で良く使われる方法で不要な参照を切る、というものです。

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
    el = null;
}

最後のnull代入によって変数elから無名関数への参照が切られ、循環参照が解決されます(参照だけでelが示していた実体、DOM自体、は残っていますが)。もうひとつこの文書で解説されている方法は実に巧妙な手法で、関数呼び出し(とそれによって生成されるクロージャ)を、参照されない他のクロージャの中で行なう、というものです。

function addHandler() {
    var clickHandler = function() {
        this.style.backgroundColor = 'red';
    }
    (function() {
        var el = document.getElementById('el');
        el.onclick = clickHandler;
    })();
}

確かに、内側の無名関数呼び出しによって生成されるクロージャはどこからも参照されていないので、不要になれば勝手にGCされるので、ある意味実に綺麗な解決策なのですが、普通のコード慣れの感覚ではまず思いつきません。クロージャを使い慣れるとこういう発想ができるようになるものなのでしょうかね。

補足:メモリインスペクタ
昔から、プログラムの実行に伴うメモリリークへの対策となると、まあ注意深いコーディングは別として、最終的にはメモリインスペクタでメモリの使用状況をチェックする、が使われてきました。まあ、インスペクタがどの程度メモリの状況を追跡できるかはインスペクタによって随分と差があり、割付解放状況を記録するだけのものから、完全に追跡してレポートを生成するものまで色々ですが。

特に最近のGC依存の実行システムはGC自体がメモリの使用状況を追跡するので、できのいいメモリインスペクタが用意されている(あるいは別途入手可能になっている)ものです。JavaScriptの場合はどうなっているのか、と気になって調べてみると、JSの実行環境、多くの場合はWEBブラウザ、とセットになって用意されていますね。Chromeの場合には、デベロッパーツールのProfileでメモリ使用状況のスナップショットが取れるようになっています。オブジェクトの種類ごとにメモリ使用状況がまとめられて表示されますので無論クロージャの項目もあります)、普通に追跡する程度であればこれで充分な情報を得ることができるでしょう。

追記 2010/12/13

循環参照によるメモリリークは普通プロセス終了まで解消されません。この点を考えると Chrome のタブ毎にプロセスを割り付ける方法はものすごく画期的なのかも知れません。タブクローズだけで(他に影響なく)メモリリークが解消できることになりますからね。