IE で a 要素を使って相対 URL からスキームを得る2014年11月09日 20時22分

いくつかのブラウザではURLがhttpの相対リンクであってもelm.protocolが「:」や空文字になったりする

JavaScriptでリンク先URLがhttp/httpsか確認する方法 - 葉っぱ日記

この「いくつかのブラウザ」とは IE のことを指していると思われますが、href プロパティに絶対 URL を設定しなおすことで、URL のスキーム部分を取得できるようになります。

// http://www.example.org/ 上で実行しているものとする。
var elm = document.createElement('a');
elm.href = '/foo?bar#baz';

// IE 6 / 7 で絶対 URL を設定。
// elm.href = elm.cloneNode(false).href でもよい。
elm.href = elm.getAttribute('href', 4);

// IE (少なくとも 8 ~ 11) で絶対 URL を設定。
elm.href += '';

elm.protocol // => "http:"
elm.hostname // => "www.example.org"
elm.href     // => "http://www.example.org/foo?bar#baz"

しかし IE 6 / 7 の、getAttribute メソッドの第 2 引数に値 4 を渡すことで絶対 URL の値が返るという挙動は、いつ実装されいつ文書化されたのでしょうか?

modern.IE で配布されている IE 6 SP 3 検証環境では確かに動作するのですが、「IE の getAttribute / setAttribute」を書いた時点 (2005 年 10 月) では文書化されていなかった気がします。文書化されていたのなら、「IE 6,7 で相対URL -> 絶対 URL の変換 - #生存戦略 、それは - subtech」にまとめられたような cloneNode メソッドを使った手法を考えることもなかったでしょうから。

なお、「続・IEでのa要素の各属性について - 文殊堂」で言及されていますが、IE 6 / 7 では iframe 要素内の文書で上述の相対 URL 解決手法を用いると、(a 要素を文書木に挿入しない限り) 解決結果のホスト部分が親文書のものになってしまうようです。(ホスト部分が親文書のものになるという現象自体は IE 11 でも発生します。)

呼ばれていないけど、私もコードゴルフしてみました2013年08月09日 03時06分

Code 2013 というイベントで出されたというコードゴルフのお題「JavaScript でデジタル時計」を見ているうちに、自分でもやりたくなったので挑戦してみました。

基本方針

出力が複数行にわたるなら 1 行ずつ処理していくのが素直な手ですが、このお題においてはそれだとひとつの文字に対する処理が細切れになってしまいます。

1 行ずつ処理していく場合のイメージ図

そこで、ある文字の出力処理をまとめてやってしまいましょう。行ごとにではなく列ごとに処理を進めていくのです。

1 列ずつ処理していく場合のイメージ図

20 分

方針が決まれば早速コーディング。以前、渋谷から 10 分のゴルフ場で似たようなお題「banner」に取り組んだときは三十六進数表記を使いましたが、とりあえずは読み書きしやすいよう二進数表記で進めます。

本番の制限時間は 20 分だったそうなので、こちらも 20 分で到達したところはこちら、

t = 0;
setInterval(function () {
    s = ' ■';
    p = [
        '111111000111111',
        '111110000000000',
        '111011010110111',
        '111111010110101',
        '111110010011100',
        '101111010111101',
        '101111010111111',
        '111111000010000',
        '111111010111111',
        '111111010111101',
        '000000101000000',
    ].map(function (b) { return parseInt(b, 2) });
    l = ['', '', '', '', ''];
    d = [
        t / 60 / 10 | 0,
        t / 60 % 10 | 0,
        10,
        t % 60 / 10 | 0,
        t % 60 % 10 | 0,
    ];
    for (i in d) {
        v = p[d[i]];
        for (j = 20; j--;) {
            l[j % 5] += s[v & 1];
            v >>= 1;
        }
    }
    document.body.innerHTML = l.join('<br>');
    t++;
}, 1000);

スペース・改行を取り除いて 458 バイト (UTF-8 でエンコードした場合) と、ちょっと詰めきれていません。二進数表記文字列 + map メソッドの組み合わせを十進整数リテラルに書き換えるだけで 284 バイトになるので、あと一歩ではあったのですが。

改良

このお題ではひとつの文字を表すのに、3 列 5 行、15 個の点を使っています。ひとつの文字に対するパターンが 15 ビットで表現可能ということは、JavaScript の 1 文字 (16 ビット) に収まるわけですね。

ほかにも、変数 sp はそれぞれ 1 箇所でしか使っていないので、変数を使わずリテラルを直接書くといった節約の成果がこれです。

t = 0;
setInterval(function () {
    l = ["", "", "", "", ""];
    for (i in d = [t / 600, t / 60 % 10, 10, t % 60 / 10, t++ % 10])
        /* "\u7E3F\u7C00\u76B7\u7EB5\u7C9C\u5EBD\u5EBF\u7E10\u7EBF\u7EBD\u0140" */
        for (v = "縿簀皷纵粜庽庿縐线纽ŀ".charCodeAt(d[i]), j = 20; j--; v >>= 1)
            l[j % 5] += " ■"[v & 1];
    document.body.innerHTML = l.join("<br>")
}, 1e3)

スペース・改行を取り除いて 230 バイトまで減らせました。さらに、setInterval の第 1 引数を関数ではなく文字列にすることで、220 バイトとなります。

上述の版では文書読み込み完了から 1 秒間が空いて時計の描画が始まります。サンプルと同じく文書読み込み完了と同時に時計が描画される、修正版 (229 バイト) を作りました。v >>= 1v /= 2 にするという変更も入れています。

追悼 E4X (仮) 発表資料2013年05月27日 21時49分

Firefox 21 で E4X のサポートが削除されたのを受け、「追悼 E4X (仮)」というイベントが開催されました。東京での開催だったのですが、私自身仕様の邦訳を手がけるなど E4X には並々ならぬ想いがあり、京都から駆けつけた次第です。

来たからにはと私も「E4X と autovivification」という題で LT をしてきました。Perl でいうところの autovivification という機能が E4X にも備わっているという話です。ほかに「私と E4X」という発表 (むしろ自分語り) もしたのですが、こちらはその場限りのオフレコです。

追悼というだけあって皆さん E4X に対する熱い思いを語っていましたが、特に感心したのが Vimperator の対応の話です。それまで E4X を使っていた部分を、ECMAScript 6 での採用が検討されているテンプレートリテラルに置き換えたものの、それ自体現在の SpiderMonkey では実装されていません。そこで、chrome://liberator/....js と JavaScript ファイルを読み込んでいたのを、liberator://....js と独自プロトコルを介した読み込みに変更し、そのプロトコルハンドラの中でソースコード変換を行っているとのことでした。

私も製品のコード中で E4X を使っており、結果として後進に負の遺産を残すこととなってしまったのですが、一方で E4X があったから今の私があるというのもひとつの真実であり、E4X 仕様及びそれを実装した Mozilla に深く感謝します。

jQuery のバグを見つけてから修正されるまで2013年01月28日 02時17分

1 月 24 日に開催された Kyoto.js meetup 4 で「jQuery のバグを見つけてから修正されるまで」と題した発表を行いました。

jQuery へのコミットに関して 2 行でまとめるとすれば次のようになるでしょうか。

  1. jQuery のソースコードはショートコーディングの嵐なので心してかかる
  2. Contributing to jQueryjQuery Core Style Guidelines は必読

発表の筋書きは以下の通りです。


jQuery のバグを見つけてから修正されるまで


フォロー・ミー

  • nanto_vi (TOYAMA Nao)
  • 株式会社はてな アプリケーションエンジニア
    • クライアントサイド (JavaScript)
    • サーバーサイド (Perl)
  • jQuery をバリバリ使っている
    • Deferred
    • イベント
    • DOM 操作

ある日どこかで

街中にクリスマスの装飾が灯り始めるころ

うんうん、変わったよね

……


そういえば Deferred を使ったとき this の値はどうなるんだったっけ?

$.Deferred().done(function () {
    this // ← ココとか
}).then().done(function () {
    this // ← ココの値は?
});

jQuery 1.8.3 と jQuery 1.9 Beta 1 で結果が違う!

汚れなき悪戯

ふむふむ…… (コードを読む)

ふむふむ…… (コードを読む)

ふむふむ…… あれ?


これ、自分の書いたコードが動かなくなる?

$.Deferred().then(function () {
    // ここでの this が
    return $.Deferred().resolveWith(this, arguments);
}).done(function () {
    // ここにも引き継がれてほしい
    this
});

……


まずい!

バック・トゥ・ザ・フューチャー

どこを直せば自分のコードが動くのか

コードとにらめっこ

にらめっこ

にらめっこ

……


問題なのはここ、でもここを変えると先の変更の意味が失われるから……


こことここか!

羊たちの沈黙

直すならテストを書かないと

テストを書くならテストを走らせないと

テストを走らせるなら jQuery のビルド環境を作らないと

grunt? Node.js なら入ってるし楽勝でしょ

……


うわあ、この Node.js バージョンが古い!

うわあ、この OS バージョンが古い!

うわあ、この Python (ry

……


沈黙


そしてクリスマスが過ぎ、正月が過ぎた

風と共に去りぬ

やっぱり正月松の内は休まないとね

ってなになに……


げげー、1.9-stable ブランチ!


もはや一刻の猶予も許されない

禁じられた遊び

ビルド環境がないならテスト環境を作ればいいじゃない

俺にはこの SpiderMonkey JavaScript Shell がある!


秘技、テストのコピペ改変!

荒野の用心棒

nanto_vi 「これ直さへんとあかんちゃう?」

J 「プルリクエストにしてーな」

nanto_viプルレクったでー

D 「ここ冗長やろ。もっと削れるやろ

nanto_vi (そない言われても…… これ以上どう切り詰めろいうんや……)

nanto_vi (しゃあないな、この条件分岐をもっと前に持ってきて……)

nanto_vi (あれ、この変数、フラグに流用できるちゃうか……)

nanto_viどや!

D 「まあええんちゃうか」

素晴らしき哉、人生!

D 「修正取り込んだで。おーきに」

J 「僕と契約して貢献者になってよ!


めでたしめでたし


※ この物語は実話を基にしたフィクションです。

jQuery で HTTP 接続するときの書き方2012年12月16日 19時41分

12 月 13 日に Kyoto.js の第 3 回 meetup で、「jQuery で HTTP 接続するときの書き方」と題した 5 分間のライトニングトークを行いました。以下にその内容を一部再構成して収録します。


こんにちは、nanto_vi です。今日は jQuery で HTTP 接続をするときの書き方について話します。

皆さん jQuery を使うことも多いかと思います。jQuery で HTTP 接続をするとき、古いサンプルだと次のような書き方が載っています。

$.ajax({
    url: '/foo/bar',
    data: { baz: 'qux' },
    success: function (data) {
        console.log(data);
    },
});

接続完了時の処理をコールバック関数として $.ajax() に渡してやる形ですね。しかし、現在この書き方は非推奨となっており、替わりに次のように書きます。

$.ajax({
    url: '/foo/bar',
    data: { baz: 'qux' },
}).done(function (data) {
    console.log(data);
});

$.ajax() の返り値に対して、done メソッドで接続完了後の処理を登録する形です。この書き方の何がいいかといえば、返り値を使いまわしたり done メソッドを複数回呼び出したりして、完了後の処理を後から追加できるところです。昔の書き方では完了後の処理がコールバック関数として $.ajax() の内部に格納されていましたが、現在は「完了後の処理」だけを $.ajax() の外部に (「プロミス」として) 取り出すことが可能になったわけですね。

今「内部の処理を外部に取り出す」と言いましたが、この言葉はどこかで聞き覚えがありませんか。そう、内部イテレータと外部イテレータです。

内部イテレータは、オブジェクトが個々の要素に対する処理を受け取り、オブジェクト内部で自身の各要素に適用させる形、

// 内部イテレータの使用例
$('p').each(function () {
    doSomethingWith(this);
});

外部イテレータは、オブジェクトから「各要素を列挙する」という機能だけをオブジェクト外部に取り出す形です。

// 外部イテレータの実装例と使用例
$.fn.iterator = function () {
    var i = 0, n = this.length, self = this;
    return {
        hasNext: function () { return i < n; },
        next: function () { return self[i++]; }
    };
};

var iterator = $('p').iterator();
while (iterator.hasNext()) {
    var element = iterator.next();
    doSomethingWith(element);
}

JavaScript 1.7 以降ではジェネレータという機能により、内部イテレータのような書き方で外部イテレータを生成することができます。

さて、「処理を内部に持ってしまっている」というのは、イベントハンドラの登録も同じですね。

$(document).on('click', function (event) {
    ...
});

イベント発生時の処理をコールバック関数として jQuery オブジェクト内部に渡していますが、これを外部に持ち出すことはできないのでしょうか。

実はそれを可能にするものとして Reactive Extensions (Rx) があります。Rx 入門記事の図にもあるように、Rx を使うと空間にまたがる要素 (配列など) の列挙と時間にまたがる要素 (イベントなど) の列挙を統一的に扱うことができます。

Reactive Extensions は主に .NET Framework 上で利用されていますが、JavaScript での実装として RxJS が公開されています。

ObserverパターンとIteratorパターンは同じだったんだよ!なんだってー!

neue cc - .NET Reactive Framework メソッド探訪第一回:FromEvent

そう、ObserverパターンとIteratorパターンは同じなのだよ、ナンダッテー!

neue cc - Reactive Extensions for JavaScript

(このあたりで時間切れ)