Generator Function 中の return 句について

新年早々、まさかりを投げてる感があって心苦しいけど、気になったので…。

さて、generatorsの説明は他に任せるとして、いきなりコードです。

function* es6_generator() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  return 5;
}

これを複数回実行して時間を計測するコードはこちら。

console.time('es6_generator');
for (i = 0; i < 1000000; i++) {
  iterator = es6_generator();
  obj = {};
  while (!obj.done) {
    obj = iterator.next();
  }
}
console.timeEnd('es6_generator');

このジェネレータオブジェクトをwhileで単純に回すと、以下のようになりますね。

{ done: false, value: 1 }
{ done: false, value: 2 }
{ done: false, value: 3 }
{ done: false, value: 4 }
{ done: true , value: 5 }

で、最後の { done: true , value: 5 }value は値として有効であるか、否か、です。

結論を言うと、本来的には無効です。

本来的にはというのは、イテレーションをするための構文である「for-of 文での動きでは」ということです。

var iter = es6_generator();
for (var v of iter) {
  console.log(v);
}
// 1
// 2
// 3
// 4

for-of でデバッグプリントすると 4 で終了し、5 は出てこないはずです。

for-of の動き

  1. Let oldEnv be the running execution context’s LexicalEnvironment.
  2. Let V = undefined .
  3. Repeat
    1. Let nextResult be the result of IteratorStep(keys).
    2. ReturnIfAbrupt(nextResult).
    3. If nextResult is false, then return NormalCompletion(V).
    4. Let nextValue be the result of IteratorValue(nextResult).
    5. ReturnIfAbrupt(nextValue).
13.6.4.7 Runtime Semantics: ForIn/OfBodyEvaluation

a の部分で、IteratorStep を呼び出していますが、これはイテレーションを一つ進めて、done プロパティが true なら false, false なら true を返します。つまり、IteratorStep が false を返したら、イテレーションは終了するということです(c. If nextResult is false, then return NormalCompletion(V). 部分)。

イテレーションを手動で書くなら

var iter = es6_generator();
var result = iter.next();
while (!result.done) {
  // ... 処理
  result = iter.next();
}

もっとスマートに書くなら

var iter = es6_generator();
var done, value;
while ({done, value} = iter.next(), !done) { // 分割代入は v8 ではまだ未実装なのでFirefoxで
  // ... 処理
}

と書くのが良いと思います。


最初のコードの return 5; は for-of では評価されませんので無駄です。手動でwhile文を書いて回した時のみ使えるデバッグ値のようなものとして扱えるかもしれませんので、完全に無駄とは言えませんが、やっぱり無駄です。コードを読んだ人を混乱させる可能性すらあります。オススメできません。

下記のように、条件によって即終了を意味するために return を使用するのは問題無いと思いますが、値は返さない(undefinedを返す)方が良いと思います。

function * foo () {
  var i = 0;
  while (true) {
    yield i++;
    if (i > 100) {
      return;
    }
  }
}

Firefox 29 で String.fromCodePoint, String.prototype.codePointAt が実装された

ECMAScript6th における、Unicode をもう少しうまく扱うためのメソッド2つ。従来の String.fromCharCode, String.prototype.charCodeAt の Unicode 対応版と言え、サロゲートペアとなる文字の扱いが変わっている。

今回は「吉野家」の正しい文字として http://www.unicode.org/cgi-bin/GetUnihanData.pl?codepoint=%F0%A0%AE%B7 を使うよ。

var str = "&#134071;野家";
var charCodes = [];
for (var i = 0; i < str.length; ++i) {
      charCodes.push(str.charCodeAt(i));
}
console.log("CharCodes(degit): ", charCodes);
// "CharCodes(degit): " [55362, 57271, 37326, 23478]
console.log("CharCodes(hex):", charCodes.map(n => n.toString(16).toUpperCase()));
// "CharCodes(hex):" ["D842", "DFB7", "91CE", "5BB6"]
console.log("fromCharCode:", String.fromCharCode(...charCodes));
// "fromCharCode:" "\uD842\uDFB7\u91CE\u5BB6"
                 
var codePoints = [c.codePointAt(0) for (c of str)];
console.log("CodePoints:", codePoints);
// "CodePoints:" [134071, 37326, 23478]
console.log("fromCodePoint:", String.fromCodePoint(...codePoints));
// "fromCodePoint:" "\uD842\uDFB7\u91CE\u5BB6"

console.log("fromCharCode === fromCodePoint:",
  String.fromCharCode(...charCodes) === String.fromCodePoint(...codePoints));
// "fromCharCode === fromCodePoint:" true

String.prototype.codePointAt は、対象がサロゲートペアな場合にも綺麗にUnicodeのコードポイントを返してくれる。String.fromCodePoint はその逆。

ただし、String.prototype.codePointAt(サロゲートペアの後部のIndex値) とした場合、charCodeAt と同じ値を返してしまう。

var str = "&#134071;野家";
var codePoints = [];
for (var i = 0, len = str.length; i < len; ++i) {
  codePoints.push(str.codePointAt(i);
}

このようなコードを書くと、結果は "CodePoints:" [134071, 57271, 37326, 23478] となってしまい、うまくない感じになる。
Firefox 27(Nightly) における新たな String.prototype["@@iterator"] - hogehoge で紹介した文字列イテレータを使用して、以下のように for-of で書くべき。

var codePoints = [];
for (var char of str) {
  codePoints(char.codePointAt(0));
}
// または

var codePoints = [for (c of str) c.codePointAt(0)];
// Firefox実装では [c.codePointAt(0) for (c of str)];

Re: __proto__ が変わりゆく件について

さすがにちょっとツッコミを入れたい。

参考: draft_proto_spec_rev2.pdf

※ まだドラフトで確定ではないのでご注意ください。

http://nextdeveloper.hatenablog.com/entry/2013/12/15/191719

注釈があるとはいえ、今この時期にこのPDFは古いです。

現在のES6 Draft仕様のリビジョンは21です。rev2の仕様を引っ張り出してくるのはどうかと…。

大筋では特に言うことは無いのだが、以下の点に関してはツッコんでおきたい

  • Enumerable: true はありえない(for-in で列挙されてしまうよ!?)
  • UnderscoreProtoEnabled という内部プロパティは、消されている

ついでに、最近のドラフトでは、__proto__のデスクリプタは [[Enumerable]]: false, [[Configurable]]: true な Setter/Getter とされている。Setter/Getterなので [[Writable]]: true でもない。

ここから先は私見

さらに、ES5.1の Object.getPrototypeOf とともに、ES6 ではObject.setPrototypeOf も定義される。よって、__proto__ プロパティは要らない子である。

明日から、JavaScript コードの先頭に

delete Object.prototype.__proto__;

と書くことを心がけよう!

:wq

Ctrl + Tab でタブの選択履歴からタブ切り替え

というツイートを見かけて。興味を持って Firefox 拡張を作ってみた :-P

使いたければどうぞ。
ただ、サポートとかするつもりが全くないし、例によって勝手に削除するかもしれない。

Vimperator

nnoremap <C-Tab> :js CTTab.start()<CR>

とかすると良いんじゃないですかね。

MacでDockにしまったWindowを復元するショートカットを作った

Cmd + M で「しまう」のショートカットがMacでは共通してあるわけだが、逆に戻すショートカットが無い。

ゴテゴテとウィンドウが重なっている状態があまり好きじゃなくて、都度「しまう」を行っていたりするわけだけど、Cmd + Tab でアプリケーションを切り替えた後、元に戻すのにマウスポインタを動かす必要が出てきて QoL が下がっていた。

ということで、ショートカットを作ろうと努力をしてみたよ。(完全ではないけど概ね動く)

やったこと

  1. AutomatorAppleScript を動かすサービスを作る
  2. システム環境設定 -> キーボード -> ショートカット の「サービス」からショートカットを割り当てる

Automator で AppleScipt

起動

種類を聞かれるので、「サービス」を選ぶ

AppleScript を書く

ユーティリティにある「AppleScriptを実行」を右側にドラッグ&ドロップしてコードを書く。

AppleScript
on run {input, parameters}
  tell application "System Events"
    set frontApplication to a reference to (processes whose visible is true and frontmost is true)
    if (count frontApplication) is 0 then return
    set appId to bundle identifier of (item 1 of frontApplication)
  end tell
  
  tell application id appId
    set hiddenWindows to a reference to (windows whose miniaturizable is true and miniaturized is true)
    try
      if (count hiddenWindows) is not 0 then
        set miniaturized of hiddenWindows to false
      end if
    on error msg number n
      activate
      display dialog msg
    end try
  end tell
  return input
end run
やっていること
  1. System Events から最も全面にあるアプリケーションのidを得る
  2. 得たidのアプリケーションの内、「しまう」ことができるもの、かつ、しまわれた状態のWindowリストを得る
  3. そのWindowリストに対して、miniaturized を false に設定する(しまわれた状態を元に戻す)

しまわれたウィンドウを一つ一つ戻すこともできるだろうけど、そのアプリケーションのは全て戻すほうが使い勝手が良いかな(?)と思ってそうしている。

ショートカット登録

システム環境設定 -> キーボード -> ショートカットを開く
左パネルの「サービス」にAutomatorで作成したものがあるので、それに対してショートカットを割り当てる


:wq

super キーワード

ECMAScript 6th には super キーワードがある。もとからキーワードとしてはあったけど、6th から挙動をもつキーワードとなった。

期待通り、プロトタイプチェーン上のプロパティアクセスまたはメソッドコールに使用できるキーワードである。

使用可能な箇所は:

  • MethoDefinition中
    • (普通の)メソッド
    • getter
    • setter

MethodDefinitionというのは

({
  method () {
  },
  get foo () {
  },
  set foo (val) {
  }
})

というように、ECMAScript6thから登場する書き方である。

super の使用方法は2種類ほどある。

  1. プロパティアクセス:継承先のプロパティへアクセス
    • super [ Expression ]
    • super . IdentifierName
  2. コンストラクタ/メソッド実行:継承先の同名のメソッド/関数を実行
    • new super Argumentsopt
    • super Arguments

super単体にアクセスする構文はないことに注意。

class Foo {
  constructor (name) {
    this.greeting = "Hello";
    this.name = name;
  }
  getClassName () {
    return "Foo";
  }
}
class Bar extends Foo {
  constructor (name) {
    super(name);
  }
  getClassName () {
    return "Bar[" + super() + "]";
  }
  sayGreeting () {
    console.log(this.greeting + " " + this.getClassName());
  }
}

super から参照できるオブジェクトは静的

以下のケースを考えてみる

var bar = new Bar("aaa");
var getClassName = bar.getClassName;
getClassName();

実はこれ、きちんと "Bar[Foo]" が返る(はず)

this のケースだったら実行のされ方によって変化しうるので想定通りには動かないのだが、superの場合はそうはならない。

内部的にはメソッド定義時に内部で super が使用されている場合、そのメソッドの内部プロパティ[[HomeObject]]に親オブジェクトが設置される。BarクラスのgetClassNameであれば、Bar.prototypeが設置されるわけだ。
そして super() は、そのメソッドにある[[HomeObject]]からプロトタイプチェーン上の一つ上へ上がったオブジェクトの同名のメソッドを取得して実行する。

この内部プロパティはECMAScript6thになって新設されたプロパティだ。上記 super の仕組を実現するために存在している。
また、[[HomeObject]]は一部の例外を除き再設置することはできない。

superの動きを this を使ってエミュレートするのは難しい

最初のコード例で行くと、super の仕組は this を使って実現できる。

class Bar extends Foo {
  getClassName () {
    return Object.getPrototypeOf(Object.getPrototypeOf(this)).getClassName.call(this);
    // return this.__proto__.__proto__.getClassName.call(this);
  }
}

しかし、これは多段の継承をしていくと破綻する。(これまでのJavaScriptでクラスを作ろうとした人はこの問題に一度は直面した経験があるだろう)

class ExBar extends Bar {
  getClassName () {
    return Object.getPrototypeOf(Object.getPrototypeOf(this)).getClassName.call(this);
  }
}

さらにBarを継承したExBarを作って、そのインスタンスから getClassName() を呼んでみよう。ExBar の getClassName は正しくBarのgetClassNameを呼び出すだろう。しかし、BarのgetClassNameはFooのgetClassNameを呼び出すことはできず、再度BarのgetClassNameを呼び出す結果となってしまう("too much recursion"などの例外が発生するだろう)

[[HomeObject]]を使ったsuperでは、メソッド定義時に設置された親オブジェクトから一つ上の[[Prototype]]を参照すれば良く、安定して使えるわけだ。

制限

ECMAScript6thになって、Functionオブジェクトの生成方法が増えている

  1. function
    • FunctionDeclaration
    • FunctionExpression
  2. MethodDefinition
  3. ArrowFunction

この内、FunctionDeclaration はオブジェクトのプロパティとして宣言できないので、実質3つの書き方があるわけだ。

だが、これまでsuperの説明にMethodDefinitionを使用してきたのにはわけがあって、[[HomeObject]]が設定されるのは、このMethodDefinitionのみなのだ。

{ foo: function () { } }といったオブジェクトリテラル上にFunctionExpressionを使用する従来のやり方では[[HomeObject]]はセットされない。
また、ArrowFunctionは、this と同様 super もレキシカルに解決するために、こちらも[[HomeObject]]はセットされない。(参照:ES.next Draft仕様より、普通のfunctionとArrowFunctionの違い - hogehoge)

まぁ、super を使うなら、class構文で定義したメソッドだろうし、class構文では MethoDefinition しか使えないわけで、あまり気にすることはないと思うけどね。

以上、自分が仕様を読んで super について把握したもの、まとめでした。

Firefox 25 個人的まとめ(ECMAScript的何か)