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 の動き
13.6.4.7 Runtime Semantics: ForIn/OfBodyEvaluation
- Let oldEnv be the running execution context’s LexicalEnvironment.
- Let V = undefined .
- Repeat
- Let nextResult be the result of IteratorStep(keys).
- ReturnIfAbrupt(nextResult).
- If nextResult is false, then return NormalCompletion(V).
- Let nextValue be the result of IteratorValue(nextResult).
- ReturnIfAbrupt(nextValue).
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 = "𠮷野家"; 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 = "𠮷野家"; 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__ が変わりゆく件について
さすがにちょっとツッコミを入れたい。
※ まだドラフトで確定ではないのでご注意ください。
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 が下がっていた。
ということで、ショートカットを作ろうと努力をしてみたよ。(完全ではないけど概ね動く)
やったこと
- Automator で AppleScript を動かすサービスを作る
- システム環境設定 -> キーボード -> ショートカット の「サービス」からショートカットを割り当てる
Automator で AppleScipt
起動
種類を聞かれるので、「サービス」を選ぶ
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
やっていること
- System Events から最も全面にあるアプリケーションのidを得る
- 得たidのアプリケーションの内、「しまう」ことができるもの、かつ、しまわれた状態のWindowリストを得る
- そのWindowリストに対して、
miniaturized
を false に設定する(しまわれた状態を元に戻す)
しまわれたウィンドウを一つ一つ戻すこともできるだろうけど、そのアプリケーションのは全て戻すほうが使い勝手が良いかな(?)と思ってそうしている。
super キーワード
ECMAScript 6th には super キーワードがある。もとからキーワードとしてはあったけど、6th から挙動をもつキーワードとなった。
期待通り、プロトタイプチェーン上のプロパティアクセスまたはメソッドコールに使用できるキーワードである。
使用可能な箇所は:
- MethoDefinition中
- (普通の)メソッド
- getter
- setter
MethodDefinitionというのは
({ method () { }, get foo () { }, set foo (val) { } })
というように、ECMAScript6thから登場する書き方である。
super の使用方法は2種類ほどある。
- プロパティアクセス:継承先のプロパティへアクセス
- super [ Expression ]
- super . IdentifierName
- コンストラクタ/メソッド実行:継承先の同名のメソッド/関数を実行
- 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オブジェクトの生成方法が増えている
- function
- FunctionDeclaration
- FunctionExpression
- MethodDefinition
- 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的何か)
- 885553 - Implement ES6 Array.prototype.find and Array.prototype.findIndex
- 866849 - Implement ES6 Array.of
- 884279 - renaming future to promise
- 886949 - Add Number.parseInt and Number.parseFloat
- 885798 - Add new ES6 Number constants
- Number.EPSILON = 2.220446049250313e-16
- 866847 - Implement Map#forEach and Set#forEach
- 578700 - (harmony:typedobjects) [meta] Harmony typed objects (nés binary data)
- 894026 - Implement BinaryIntegerLiteral and OctalIntegerLiteral