Kanasan.JS JavaScript 第 5 版読書会 #5 ― 2008年07月25日 11時09分
Kanasan.JS JavaScript 第 5 版読書会 #5 に行ってきました (当日のチャットログ、参加者のブログ記事一覧)。今回からはいよいよ第 2 部、クライアントサイドスクリプトということで、13 章全体を読みました。
控えめな JavaScript
JavaScript をどのように使うかの指針として、「控えめな JavaScript (unobtrusive JavaScript)」(WaSP DOM Scripting Task Force の声明文) というキーワードが挙げられています。
- HTML マークアップと JavaScript コードを分離する。
- 正常に機能を停止する。ブラウザがある機能を持たない、もしくは JavaScript 自体が動作しない場合であっても、コンテンツが利用可能であるようにする。
- アクセシビリティを低下させるのではなく、向上させる。
これは非常に重要な指針であり、通常の Web ページでスクリプトを使う際にはぜひ従うべきものだと思います。とはいえ、文書ではなくアプリケーションを提供するときにはスクリプトの使用を必須とせざるを得ない場合もあり、悩むところです。
document.write
の挿入位置
document の write メソッドで出力された内容は、多くのブラウザで script 要素の終了直後に挿入されます。仕様ではどうなっているかというと、HTML 4.01 の文書の動的変更に関する箇所 (原文) ではややあいまいながらも script 要素を出力内容で置換するように読み取れます。DOM HTML の write メソッドの定義では読み込み中の文書に対する動作が記されていません。HTML 5 のwrite メソッドの説明ではそのあたりの詳細も定義される予定です。
JavaScript の MIME タイプ
RFC 4329 Scripting Media Types では text/javascript は非推奨となっていますが、IE が application/javascript に対応していないので、当分は text/javascript を使わざるを得ないでしょう (cf. オレオレ言語の MIME タイプ)。
script 要素の defer 属性
スクリプトの実行を後回しにして、先に文書の解析を続行できることを示す属性です。実行を延期されたスクリプトは、実行を延期できないスクリプトに出くわした時点で実行されます
とありますが、HTML 4.01 での defer 属性の定義 (原文) にそのようなことは書かれていません。文書の動的変更に関して、すべての SCRIPT 要素は、文書が読み込まれる際の順番で評価される
という文と組み合わせての解釈のようです。HTML 5 の defer 属性の定義では IE の実装に合わせる形で動作が再定義されています。
noscript 要素
「控えめな JavaScript」の考え方に従うなら、noscript 要素を使わなくてもスクリプト無効時の閲覧に支障が出ない文書の作成を目指すべきだと思います。
document.domain
通常、ドメインの異なるページ間ではオブジェクトにアクセスできませんが、アクセスする側される側とも document の domain プロパティに同じ値を設定することで、アクセスできるようになるそうです。ただし、domain プロパティに設定できるのは範囲を広げる方向のみ、つまり、www.example.org のページでは example.org には設定できるけど example.com はだめとのこと。使ったことのないプロパティなので動作もよく知りませんでした。
Same Origin Policy
異なるリソース間でのアクセスで重要となってくる same origin policy、サイ本や竹迫さんのプレゼンでは「同一出身ポリシー」と訳してましたが、私は「同一生成元ポリシー」という訳語を使っています。ちなみにこれを書いている時点でググってみると、「同一出身ポリシー」203 件に対して「同一生成元ポリシー」359 件でした。
プライベート変数の実現方法
KIMOTO さんによる LT。JavaScript で、外部からは見えないが、同じクラス (便宜的にこの語を使います) のインスタンス間では見えるプライベート変数を実現する試みです。コードは次のような感じでした (一部改変)。
var namespace = {};
(function () {
var seed = 0;
var p = {};
namespace.Foo = function (x) {
var id = "" + seed++;
this.toString = function () { return id; };
p[this] = {};
p[this].x = x;
};
namespace.Foo.prototype.getX = function () {
return p[this].x;
};
namespace.Foo.prototype.setX = function (x) {
p[this].x = x;
};
namespace.Foo.prototype.add = function (that) {
p[this].x += p[that].x;
};
})();
これなら p は外部から見えないので、アクセサを通さないとメンバ変数にアクセスできないというわけですね。コンストラクタ内でメソッドごとにクロージャを作る方法と違って、メソッドが増えてもインスタンス生成の時間はそれほど変わらないし、ほかのインスタンスのメンバ変数にも直接アクセスできます。この仕組みを考えた後に調べて、これが Perl では inside-out object と呼ばれる手法だと知ったとのことですが、それを自分で考え付いてJavaScriptで実現したというのはすごいです。
Perl の inside-out object との違いとして、JavaScript にはデストラクタがないため、メンバ変数の値がずっと残りっぱなしになってしまうという問題があります。対策としては、自分で後片付け用のメソッドを呼んでやるか、利用者のマシンの性能を信じるかといったところでしょうか。toString メソッドを本来の目的で使えないという声もありましたが、そこは代わりに valueOf メソッドを使って p[+this].x
とするのもありかもしれません。
ゆの in ECMAScript 解説
いつまでそのネタを引っ張るんだといわれそうですが、私も LT の時間をもらってゆの in ECMAScript の解説をしてきました。資料は次に挙げますが、これはソースコードそのものの解説というより、ソースを読むに当たっての基礎知識です。
識別子に使える Unicode 文字の「文字」というのは letter であり character ではありません。つまり、記号や空白文字は使えません。正規表現オブジェクトの exec メソッドについては取説 正規表現を参照してください。また、SpiderMonkey では関数を文字列化したときに一部の文字がエスケープされます。
(function () { return "日本語"; }).toString();
// => function () {
// => return "\u65E5\u672C\u8A9E";
// => }
以上を踏まえて処理の流れを追っていくと以下のようになります。
- 式
ゆの in languageが流行ってるって
を評価する。 ゆの.toString()
が呼び出される。- グローバル変数
_
を作り、値"ひだまり"
を設定する。 ゆの()
を呼び出す。X.valueOf()
が呼び出される。- グローバル変数
_
の値に"ひだまりスケッチ"
を設定する。 X / _
はundefined / "ひだまり"
よりNaN / NaN
となりNaN
に評価される。X.valueOf()
が呼び出される。X
自身はもはやvalueOf
メソッドを持たないので、Object.prototype.valueOf
がX
上で呼び出されX
が返される。X
はプリミティブ値ではないので、X.toString()
が呼び出される。- グローバル変数
_
の値に"ひだまりスケッチ×365"
を設定する。 NaN / X
はNaN / undefined
よりNaN / NaN
となりNaN
に評価される。NaN < "来週も見てくださいね"
はNaN < NaN
となりfalse
に評価される。ゆの.toString()
の結果としてゆの
を返す。ゆの
はプリミティブ値ではないので、ゆの.valueOf()
が呼び出される。exec
メソッドの引数としてゆの
を渡したので、ゆの.toString()
が呼び出される。ゆの
自身はもはやtoString
メソッドを持たないので、Function.prototype.toString
がゆの
上で呼び出されゆの
のソース文字列が返される。- ソース文字列がエスケープされる実装のために、引用符ごと抜き出して
eval
関数で目的の文字列に戻す。 - グローバル変数
そんなことよりヱヴァンゲリヲン新劇場版
を作り、値"ひだまりスケッチ×365 来週も見てくださいね!"
を設定する。 ゆの in languageが流行ってるって
はundefined in { undefined: true }
より"undefined" in { "undefined": true }
となりtrue
に評価される。true ? そんなことよりヱヴァンゲリヲン新劇場版 : 破は...
はそんなことよりヱヴァンゲリヲン新劇場版
となり"ひだまりスケッチ×365 来週も見てくださいね!"
に評価される。式破は...
は評価されない。- プログラム全体の結果として
"ひだまりスケッチ×365 来週も見てくださいね!"
が返される。
「ゆの in language」という言葉を式中に含めたかったので in 演算子を使ったのですが、in 演算子は真偽値しか返せません。そこで、文字列を返すために条件演算子を使い、in 演算子はその条件式に含めることにしました。破は... (以下省略)
の部分は評価されませんが全体としては条件演算子になっており、早くカヲル君が見たいです. ヱヴァンゲリヲン新劇場版
はプロパティアクセスとして、序でカヲル君が出てきたときは (以下省略)
は関数呼び出しとして解析されます。
条件演算子を使う以上コロンが必要なのですが、タイトルにコロンの入ったアニメ / マンガ / ライトノベルがヱヴァくらいしか思い浮かばなかったのでそれを使いました。後から FF:U があると教えてもらったり、JINKI:EXTEND なんてものもあったなと思い出したりしたのですが、両方とも見てないのでそのままです。
雑感
個人的な反省点として、LT の時間を超過しない、発言するときは大きな声でというのが挙げられます。LT に関しては OSC Kansai 2008 閉会式の LT のように全員にわかるタイマーがあるといいのですが、それはスクリーンが二つ以上ないと難しいので、せめて秒読みをつけたほうがいいかもしれません。時間が不定なら減算式ではなく加算式の秒読みでもあるとしまりが出るかなと思います。気軽に話せるという点からすると逆効果になってしまうかもしれませんが。
全体では前日の Shibuya.js in Kyoto からの流れで竹迫さんと amachang さん、それに名古屋の JavaScript 勉強会 DeLLa.JS の主催者 issm さんといった豪華メンバーが駆けつけてくださり、読書会、LT、懇親会いずれも大いに盛り上がりました。Kanasan さんはじめスタッフ、参加者の皆さんありがとうございました。ちなみに DeLLa.JS 次回は 8 月 2 日に開催予定とのことなので、名古屋県圏の方もそうでない方もぜひ参加してみるといいと思います。
コメント
トラックバック
このエントリのトラックバックURL: http://nanto.asablo.jp/blog/2008/07/25/3648120/tb
コメントをどうぞ
※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。
※投稿には管理者が設定した質問に答える必要があります。