Firfox 30(Nightly)で ES6的な ArrayComprehension,GeneratorComprehension が実装された

書くのが1週間ほど遅れた。

var arry = [for (value of iteratableObject) value]; // ArrayComprehension
/* ==
(function() {
  var list = [];
  for (let value of iteratableObject) {
    list.push(value);
  }
  return list;
}());
*/

var gene = (for (value of iteratableObject) value); // GeneratorComprehension
/* ==
(function *() {
  for (let value of iteratableObject) {
    yield value;
  }
}());
*/

基本的な構文は上記のような感じ。コメント部に書いたのに近いコードを短縮して書けると思えば良いと思う。

for-of 部分は続けて書けるし、if () によるフィルタリングも可能

var list = [
  ["a", "b"],
  ["c", 10],
  "d",
];

[for (child of list) for (v of child) if (typeof v === "string") v].join("/");
// "a/b/c/d"
[for (child of list) if (Array.isArray(child)) for (v of child) if (typeof v === "string") v].join("/");
// "a/b/c"

Mozilla既存のArrayComprehension, GeneratorComprehension

ES6に入る前からMozilla JavaScript 1.8[developer.mozilla.org]からこれらがあり、ES6も当初はこの構文だった。

[v for (value1 of iteratableObject) for (v of value1)];

最初に最終的に取られる値を指定した後に for-of を書くような感じだった。
ただ、これだと評価の順が混乱しやすい。

     [v for (value1 of iteratableObject) for (v of value1)];
// 1.     >--実行---------------------->
// 2.                                     >--実行-------->
// 3. 値

という順番が正しいのだが、以下の方が自然にも思える。

     [v for (v of value1) for (value1 of iteratableObject)];
// 1.                     >--実行------------------------>
// 2.     >--実行------->
// 3. 値

で、結局、現在の下記の様な順番の方が自然だよね、という話になったと記憶している(あやふやだが)

     [for (value1 of iteratableObject) for (v of value1) v];
// 1. >--実行------------------------>
// 2.                                  >--実行--------->
// 3.                                                    値

現状のFirefoxでは、既存の構文もES6の構文も両方サポートしている。拡張機能内などでは、既存構文が使われているのでしばらくは両方サポートの状態が続くと思う。

アドオンとかプラグインとか拡張機能とか

まぁあることないこと言われていて、「アドオンじゃなくてプラグインか」みたいなコメントも多く散見されるわけだが。

Mozilla 的には以下の様な定義になっている。

アドオンは、以下の 3 種類に分けられます:

  • 拡張機能
    拡張機能は、Firefox に新しい機能を追加したり既存の機能を変更したりします。拡張機能には、広告の表示をブロックしたり、Web サイトから動画をダウンロードしたり、FacebookTwitter を便利に利用するもの、また、他のブラウザに搭載されている機能を追加するものなどがあります。
  • テーマ
    外観を変更するアドオンは 2 種類あります: テーマは、ボタンやメニューなどの外観を変更し、ペルソナは、メニューバーとツールバーを背景画像で飾ります。
  • プラグイン
    プラグインは、Firefox で扱えないインターネットコンテンツを扱えるようにします。これらのコンテンツには、動画や音声、オンラインゲーム、プレゼンテーションなどの特許で保護されたコンテンツに使用される FlashQuickTimeSilverlight などの形式があります。プラグインMozilla 以外の他の会社によって作成、配布されています。
アドオンで Firefox を活用する | Firefox ヘルプ

"アドオン = 拡張機能" ではなく、"アドオン ∋ 拡張機能" である。また、"アドオン ∋ プラグイン" でもある。
よって、「アドオンじゃなくてプラグインか」というのは正しくなくて、「拡張機能やテーマではなくてプラグインか」の方が正しい。

とはいえ、この辺りはぐちゃぐちゃな言われようで、海外でも extension なのに plugin と書いていたり、アドオンがプラグインを含まないようなニュアンスであることも多い。間違えるのも無理はない感じ。(自分も拡張機能とテーマのみを指してアドオンと呼んでしまう)

はっきりと書きたければ、「拡張機能」「テーマ」「プラグイン」という言葉を用いて「アドオン」は使わない方が良いかも。

自戒を込めて

Windowsでディスク使用率等の一覧を出力するバッチファイルを書いた

仕事でディスク使用率を教えてくれっていう感じの依頼を受けることが良くあり、その都度Windowsではマイコンピュータを開いたキャプチャ画像とかを送ってやったりしていたのだが、いい加減飽きてきた。
画像じゃなくて、テキストで欲しい。
ドライブ一覧を得るコマンド(diskpart の list volume)とか使用量を見るコマンド(fsutil volume diskfree)とか、管理者権限が必要になったり、バッチ化するにもイマイチな使い勝手だし、Unixライクな df っぽいコマンドが欲しいなと思って作った。

バッチファイルだけど、JScript である。

@if(0)==(0) ECHO OFF
CScript //NoLogo //E:JScript "%~f0" %*
GOTO :EOF
@end

showDrives();

function showDrives() {
  var FSO = new ActiveXObject("Scripting.FileSystemObject");
  var e = new Enumerator(FSO.Drives);
  WScript.Echo("Drive Type   Total    Used    Free    % ValumeName");
  WScript.Echo("----- ---- ------- ------- ------- ---- ----------");
  for (; !e.atEnd(); e.moveNext()) {
    showDriveInfo(e.item());
  }
}

// Drive Type Size Used Aviable Percent
// ----- ----
function showDriveInfo (aDrive) {
  var msg;
  if (aDrive.IsReady) {
    var usedSize = aDrive.TotalSize - aDrive.FreeSpace;
    msg = [
      justify(aDrive.DriveLetter + ":", 5, "left"),
      justify(getDriveType(aDrive), 4, "left"),
      justify(toByte(aDrive.TotalSize, "G", 2), 7, "right"),
      justify(toByte(usedSize, "G", 2), 7, "right"),
      justify(toByte(aDrive.FreeSpace, "G", 2), 7, "right"),
      justify(((usedSize / aDrive.TotalSize * 100).toFixed(0) + "%"), 4, "right"),
      aDrive.VolumeName
    ].join(" ");
  } else {
    msg = [
      justify(aDrive.DriveLetter + ":", 5, "left"),
      justify(getDriveType(aDrive), 4, "left"),
      justify("-", 7, "right"),
      justify("-", 7, "right"),
      justify("-", 7, "right"),
      justify("-", 4, "right"),
    ].join(" ");
  }
  WScript.Echo(msg);
}

function toByte (aNum, aByteType, aFixSize) {
  var byteSize = 1;
  var postfix = aByteType.toUpperCase();
  switch (postfix) {
    case "K":
      byteSize = 1024;
      break;
    case "M": 
      byteSize = 1024 * 1024;
      break;
    case "G":
      byteSize = 1024 * 1024 * 1024;
      break;
    default:
      throw new TypeError("Unknown ByteType: " + aByteType);
  }
  return (aNum / byteSize).toFixed(aFixSize) + postfix;
}

function justify (aStr, aSize, aType) {
  var spaces = (new Array(aSize + 1)).join(" ");
  switch (aType) {
    case undefined:
    case 0:
    case "left":
      return (aStr + spaces).slice(0, aSize);
    case 1:
    case "right":
      return (spaces + aStr).slice(-aSize);
    default:
      throw new TypeError("Unknown JustifyType");
  }
}

function getDriveType (aDrive) {
  switch (aDrive.DriveType) {
    case 1: // Removable
      return "RDD";
    case 2: // HardDisk
      return "HDD";
    case 3: // NetworkDirve
      return "Net";
    case 4: // CD
      return "CDD";
    case 5: // RAM Disk
      return "RAM";
    case 0:
    default: // Unknown
      return "???";
  }
}

コード冒頭の謎記述は http://d.hatena.ne.jp/miya2000/20090823/p0 を参照。

出力結果

> df.bat
Drive Type   Total    Used    Free    % ValumeName
----- ---- ------- ------- ------- ---- ----------
A:    RDD        -       -       -    -
C:    HDD   60.00G  24.51G  35.48G  41%
D:    CDD        -       -       -    -
E:    CDD        -       -       -    -
Z:    Net  930.71G 658.18G 272.53G  71% Shared Folders

オブジェクトのキーの順序

上記リンク先の手法は、新たなオブジェクトにキーを配列に入れてソートした後、その順にプロパティ値として入れる方法だ。が、この方法では完全にソートすることはできない。

仕様的にも実装的にも無理である。

仕様的に

  • ECMAScript 5th 仕様には、プロパティ列挙の順序は書かれていない
  • ECMAScript 6th Draft には、9.1.11 [[Enumerate]] ()The mechanics and order of enumerating the properties is not specified but must conform to the rules specified below. と明記されている。

要するに実装まかせであり、仕様として順序は保証していない。

実装的に

上記のように仕様はないとはいえ、皆似たような順序になるよう調整されている。

概ね、strawman:enumeration [ES Wiki]の下部に載っているような順序になる。

  1. Uint32範囲内の整数になるキー値なら、Index値として順にソートされ、先にならぶ
  2. その他は定義された順

v8(Chrome), Chakra(IE11), JSC(Safari) はそんな感じ。ただし、微妙にずれがある。

v8 は 0 - 0xFFFFFFFF(2^32-1) で、Chakra, JSCは 0 - 0xFFFFFFFF-1(2^32-2) がIndex値として捉えられる範囲っぽい。

SpiderMonkey(Firefox)の謎挙動

上記では挙げなかったSpiderMonkeyであるが…。

999と1000が境界っぽく、意味不明である。(Firefox25調べ)

var obj = { "a":"", 1:"", 100:"", 999:"", 3:"" }; for (var key in obj){ console.log(key); }
// 1
// 3
// 100
// 999
// a

だが、1000 以上になると、Index値としての対象にならない模様。

var obj = { "a":"", 1:"", 100:"", 1000:"", 3:"" }; for (var key in obj){ console.log(key); }
// 1
// 3
// 100
// a
// 1000

話が脱線したが、ともかく、整数に変換可能なキーは勝手にソートされるため、オブジェクトに入れ直す手法は完全ではない。

Web標準まわりと民主主義

チャーチルの「民主主義は最悪だ。これまで施行されてきた全ての政治制度を除いて。」ってあるじゃないですか。

Web標準まわりってそれっぽいよねって思った。

Web標準を仕様として、その体現の実装としてWebブラウザがあるわけだけど、それぞれの党がこれほどまでにシェア争いをしているのは珍しいと思う。逆に、仕様と実装は1対1で対応していたり、1党独裁的なものはたくさんあるでしょう。Flash なんかは実装が1つだし、JavaなんかはOracle Javaが大多数で一党独裁みたいなものだったりと。

1対1対応や一党独裁的なもので、うまく行っているもの(生き残っているもの)は「優しい終身の独裁者」なんて呼ばれるようなリーダーや一企業が引っ張っていたりして、専制君主的だ。専制君主な政治体制はうまく運んでいるときは最高のパフォーマンスを発揮する。その代わり、ダメなときはとことんダメで使用者からすると逃げ道がなく困った状況になる。(銀河英雄伝説的な感じ)

対して民主主義は、意思決定が遅かったりとか、最高のパフォーマンスを発揮するわけじゃないけど、最悪の状況にもならない。


Webはやはり全世界の全ての人が使えるべきだし、Web標準はそれを目指していると思う。そのためには、1ベンダーによる1実装ではリスクが高過ぎる。複数のベンダーが群雄割拠健全に争うのが正しい姿だと思う。

思うけど、提出された仕様と実装の差異、実装と実装の差異が世界に悲しみを撒き散らしている現状は残念以外の何物でもなく、まったくもって「Web標準は最悪だ」

でも、それ以外に道はないよね

などと、まとまりなくモニョモニョ考えたりしてた。

Web開発をしている皆さん、頑張ってください。