7

私はいつもそれを想定して<var> += 1おり<var> = <var> + 1、JSでも同じセマンティクスを持っています。
さて、この CoffeeScript コードは、グローバル変数に適用されると、別の JavaScript にコンパイルされますe

a: ->
  e = e + 1
b: ->
  e += 1

bグローバル変数を使用するのに対しa、ローカル変数を定義することに注意してください。

({
  a: function() {
    var e;
    return e = e + 1;
  },
  b: function() {
    return e += 1;
  }
});

自分で試してみてください。
これはバグですか、それとも理由がありますか?

4

3 に答える 3

10

これをバグ、または少なくとも文書化されていないエッジケースまたはあいまいさと呼ぶと思います。ドキュメントには、CoffeeScript で新しいローカル変数がいつ作成されるかを明示的に指定するものは何も表示されないため、通常の

現在の実装がXを実行するときにXを実行しますが、これは現在の実装がそのように実行するために発生します。

みたいなこと。

新しい変数の作成をトリガーすると思われる条件は代入です。値を与えようとすると、CoffeeScript が新しい変数の作成を決定したように見えます。したがって、この:

a = ->
  e = e + 1

になる

var a;
a = function() {
  var e;
  return e = e + 1;
};

値を明示的に割り当てているため、ローカルe変数を使用eします。単にe式で参照する場合:

b = ->
  e += 1

その場合、CoffeeScript は新しい変数を作成しませんe。そこに割り当てがあることを認識しないからです。e +=1CS は式を認識しますが、 と同等と見なすほどスマートではありませんe = e + 1

興味深いことに、op=JavaScript ではなく CoffeeScript の一部であるフォームを使用すると、CS は問題を認識します。例えば:

c = ->
  e ||= 11

次のエラーが発生します。

変数 "e" は定義されていないため ||= で代入できません

同様の苦情を申し立てることe += 1は、賢明で一貫性があると思います。または、すべてのa op= b式を展開してa = a op b同等に扱う必要があります。


CoffeeScript のソースを見ると、何が起こっているかがわかります。少し調べてみると、すべてのop=構成が最終的に通過することがわかりAssign#compileNodeます。

compileNode: (o) ->
  if isValue = @variable instanceof Value
    return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
    return @compileSplice       o if @variable.isSplice()
    return @compileConditional  o if @context in ['||=', '&&=', '?=']
  #...

そのため、CoffeeScript 固有のop=条件構造には、期待どおりの特別な処理があります。簡単なレビューでは、無条件a op= b(opつまり、 、、およびop以外の) は JavaScript に直接渡すことをお勧めします。それで、何が起こっているのですか?予想どおり、宣言されていない変数を使用していないことを確認します||&&?compileCondtional

compileConditional: (o) ->
  [left, right] = @variable.cacheReference o
  # Disallow conditional assignment of undefined variables.
  if not left.properties.length and left.base instanceof Literal and 
         left.base.value != "this" and not o.scope.check left.base.value
    throw new Error "the variable \"#{left.base.value}\" can't be assigned with #{@context} because it has not been defined."
  #...

表示されるエラー メッセージと、がどこかに定義されていない場合は-> a ||= 11許可されていないことを示すコメントがあります。a ||= ba

于 2012-11-06T20:09:50.897 に答える
3

これはドキュメントからまとめることができます:

  • =:レキシカルスコープでの代入

    CoffeeScript コンパイラは、すべての変数がレキシカル スコープ内で適切に宣言されていることを確認しvarます。自分で記述する必要はありません。

    inner一方、関数内では、同じ名前の外部変数の値を変更できないため、独自の.

    このセクションの例は、あなたのケースとまったく同じです。

  • +=||=

    これは宣言ではないので、上記は当てはまりません。がない場合+=は、 と同様に通常の意味になり||=ます。

    実際、これらは CoffeeScript によって再定義されていないため、その意味は ECMA-262 (基礎となるターゲット言語) から取得され、観察した結果が得られます。

    残念ながら、この「フォールスルー」は明示的に文書化されていないようです。

于 2012-11-06T20:25:42.837 に答える
2

この問題はごく最近、CoffeeScript の Github Issues で議論されましたコンパイラの現在の動作は、この前の問題で合意されたか、少なくとも議論されたようです。

基本的に、JavaScript では式e = e + 1e += 1は常に同等です。新しい変数を導入することはないためです。これらは常に1(ローカルまたはグローバル)e変数に追加されるか、 の場合に失敗しますtypeof e === 'undefined'。これで、式var e = e + 1は JavaScript で有効になり、変数を宣言し、それを追加のe値に代入します( 、明らかに =P)。 whileは構文的に無効です。undefined1NaNvar e += 1

CoffeeScript では、以前に宣言されていないe = e + 1場合は変数宣言にすることができます。また、現在のスコープで定義されている場合は代入ステートメントにすることもできますが、新しい変数を導入することはありません (変数をインクリメントする意味がないため、やや合理的な動作です)。以前に宣言されていない変数)。eee += 1

これは、私が理解している現在の動作です。e = e + 1と が別のことを意味するのはちょっと残念だと思いますe += 1が、暗黙の変数宣言と JavaScript のスコープ規則の組み合わせの結果であることは理解しています (これは、おそらくかなり偏った説明に対するコメントです)。

于 2012-11-06T20:54:16.683 に答える