Javascript (ECMAScript) の変なところ

| コメント(0) | トラックバック(0)

Javascript (ECMAScript) には変なところがたくさんある。
ここでは、クロージャーやプロトタイプチェーンといった大きいところではなくてもっと細かい変なところをまとめたい。
以前、自分がパーザーや制御文を実装していたためその辺りが多いかも。

ECMA 262 5th

Completion value

Completion value というのは、

//
100; // line. 1
200; // line. 2

とあった時に、line. 1を実行するとCompletion valueが100になり、line. 2を実行すると200になる、というものである。
まあ、 Perl の return を省略した場合の戻り値ようなものだ。

基本的に参照する方法もないし、言語実装の内部的なものなのでどうでもいいようなものだが、実は eval が返す値のことだったりする。
eval("return 10;"); がエラーになるのは、evalは文字列をjsファイルのトップレベルとして評価するためだが、 eval("10;");10 が帰るのはプログラムを実行した最後のCompletion valueを返しているのだ。

厄介なのが、DOMのイベントのIE時代にトラディショナルなコードで、
<button onclick="false;" />
など書いて動いていた(今ではjQueryのbind, live, delegateを使う)が、これは、

// ex.
function (event) {
    false;
}

と書いてあるのと同じことで、この関数の戻り値はundefinedでありfalseではない。
結局、コードがソースコードのどういう構文要素なのかを考えないと誤りに気付きにくい。

参考:
6.1.6.1 Event handlers

Type conversion

Type nameDescription
Booleanブール型
Number浮動小数点型。整数型はない(実装内部にはあることが多い)
String文字列型
Objectオブジェクト型

Javascriptでは特に型を考慮する必要はないが、内部的には上記の型がある。

// Clearly, 'a' contains a string primitive value.
var a = "string";

しかし、演算子や組み込み関数の中には内部的な型変換を呼ぶものがあるため、definePropertyなどしてあるとカオスなことができる。

今度書く

Regular Expression Literals

人間が見ても割り算やコメントと区別が付きづらいが、それはパーザーにとっても同じだったりする。

// ex.
/=/==/=/;

の実行結果は false だ。これは == の左右の正規表現オブジェクトの比較となり、同一のオブジェクトではないためこのようになる。

しかし、以下の2つはどうだろうか?

// ex.
/=//==/=/; // case 1
/=//=/=/; // case 2

これらをFirefox と Chormeで試すと、Firefoxでは両方同じエラーになるが、Chromeではそれぞれ異なる。

# result (Firefox)
>> Exception: ReferenceError: invalid assignment left-hand side
>> Exception: ReferenceError: invalid assignment left-hand side
# result (Chrome)
>> Exception: SyntaxError: Unexpected token =
>> Exception: ReferenceError: Invalid left-hand side in assignment

Firefox は、 /=//= までパーズして、 /= の左辺がオブジェクトだから代入できないということで ReferenceError を出している。一方、Chrome は /=//= までパーズして、次の@=@が受理できずに SyntaxError となっている。

どちらが正しいということはないが、case 1 の方は JS の構文として誤りなので、異なるエラーが出た方がいい気もする。パーザーのアルゴリズムには LL, LALR などあるが、その違いにより異なっているのだろう。

Automatic Semicolon Insertion

今度書く

Labelled Statements

JSには Labelled Statement というものがある。
これがどういうものかというと、

// example
foo:
for(;;) {
    for(;;) {
        print("hello.");
        break foo;
    }
    print("ouch!");
}

で、内側のbreakで外のループも抜けるというものだ。これも perl から借りてきたような感じがする。 C プログラマには違和感があるし、特に使っているコードを見た覚えがない。いろいろあると思うが、関数を分けてreturnするとか、例外を投げることが多いような気がする。

実は continue でも使える。ちょっと見てみよう。

// example
foo:
for(var i = 0; i < 3; i++) {
    for(;;) {
        print("hello.");
        continue foo;
    }
    print("ouch!");
}

実行すると、

bc.
hello.
hello.
hello.

となる。これくらいだったらまあ分かると思うが、もっと大きいループでこれをやられると悪夢ではないだろうか。

ところで、ちょっとした間違いで LabelledStatement になってしまって混乱することもある。一番ありそうなのは、

// example
{ a: 1 }

というJSONを括弧で囲まないまま eval して戻り値が 1 になって首をかしげることではないかと思う。
この場合、外側の { } はコードブロックを表し a というラベルが付いた 1 というステートメントが実行され、その Completion Value である 1 が返る。

Object Literal

IE6 では、 ObjectLiteral の最後にカンマがあるとパーズエラーになってしまう。
特に jQuery では options に Object Literal を渡すことが多いので気をつけたほうが良い。
こういうのはツールでチェックするべきだろう。

// example
{
    foo: 100,
    bar: 200, // <- Causes parse error in IE6
}

DOM

Event handler

スコープに上の要素が入る。

今度書く

The window object

今度書く

その他

トラックバック(0)

トラックバックURL: http://www.fujlog.net/mt-5/mt-tb.cgi/1039

コメントする

このブログ記事について

このページは、Youichi Fujimotoが2011年10月 3日 11:56に書いたブログ記事です。

ひとつ前のブログ記事は「Media Center ソフト比較」です。

次のブログ記事は「jQuery Mobile を使って Twitter ビューワー作ってみた」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

カテゴリ

  • Bicycle
  • Book
  • Car
  • Diary
  • Life
  • Link
  • Media
    • Music
    • TV
  • Mobile
  • Motorsports
  • News
  • PC
    • Hardware
    • Programming
    • Software
  • Profile
  • Technology
  • Web

ウェブページ

Powered by Movable Type 5.12