?= と or= を CoffeeScript 1.2.0 で調べてみる
CoffeeScript は Ruby に似ているところがたくさんあり,Ruby プログラマにも比較的馴染みやすい言語だと思います.たとえば,変数を初期化するのに便利な Ruby の ||=
に対して,CoffeeScript にも予想通り or=
があります.しかし,CoffeeScript (というか JavaScript)では null
や undefined
だけでなく 0
や ""
(空文字列)も false
扱いなので困ることがあります.そんなときのために,or=
のほかに ?=
が用意されています.?
は CoffeeScript の存在確認演算子で,undefined
か null
のときにのみ false
になります.これで,0
や ""
は true
扱いできます.すなわち,
undefined? #=> false
null? #=> false
0? #=> true
''? #=> true
となります.ここで a or= b
と a ?= b
とは,それぞれ,
a or a = b
a ? a = b
と同じだと思っておいて間違いないだろう,というのがこのエントリの結論です.
本文では,or=
や ?=
を含んだ式を CoffeeScript 1.2.0 でコンパイルしてみて JavaScript でどうなっているかを確認していきますが,時間が無い人は上の結論だけで十分です.
はじめに
Tumblr に ?= と or= の違いを理解して,より CoffeeScript っぽいコードを書く というテキストをポストしたら,
(
a ?= b
とa or= b
は,)正確にはそれぞれ
- a ? a = b
- a || a = b
の略です。
とコメントをもらいました.実は元 Tumblr のほうに「a ?= b
は a = b unless a?
の短縮形」とか,「a or= b
は a = a or b
の短縮形」と書いていたんです.このコメントが無ければ,それ以上考えることは無かったので,ありがたいコメントでした.
コメントをもらって,何が正確か調べてみようと最初は考えたのですが,http://coffeescript.org/ に行っても厳密な言語リファレンスがあるわけでもなく,何か「正確」が書いてある文書も無さそうなので,CoffeeScript 1.2.0 がどのように JavaScript にコンパイルするかを調べてみることにしました.
?= を調べてみる.
まずは,?=
から調べてみます.ためしに,?=
を使った例を CoffeeScript で書いてみました.
# conditional_assignment-1.coffeea1 ?= b1 # 1)
a2 = b2 unless a2? # 2)
a3 ? a3 = b3 # 3)
これを,コンパイルしてみます.
% coffee -v
CoffeeScript version 1.2.0
% coffee -bp conditional_assignments-1.coffee
var a2, a3;if (typeof a1 === "undefined" || a1 === null) a1 = b1;
if (typeof a2 === "undefined" || a2 === null) a2 = b2;
if (typeof a3 !== "undefined" && a3 !== null) {
a3;
} else {
a3 = b3;
};
実行結果は例 1)〜3) で変わらないものの,JavaScript は例 1) a1 ?= b1
と例 2) a2 = b2 unless a2?
が同じ結果になりました.しかし,これだけで a ?= b
は a = b unless a?
と同じと言い切ってはいけません.なぜなら,a?
が false
になるときの戻り値が a ?= b
と a = b unless a?
とで異なるからです.次に,戻り値を考慮した例を書いてみます.
# conditional_assignment-2.coffeec = (a4 ?= b4) # 4)
c = (a5 = b5 unless a5?) # 5)
c = (a6 ? a6 = b6) # 6)
これを,コンパイルすると先程と様子が違います.
% coffee -bp conditional_assignments-2.coffee
var a5, a6, c;c = (typeof a4 !== "undefined" && a4 !== null ? a4 : a4 = b4);
c = (typeof a5 === "undefined" || a5 === null ? a5 = b5 : void 0);
c = typeof a6 !== "undefined" && a6 !== null ? a6 : a6 = b6;
今度は例 4) と例 6) が同じ結果を返すようになりました.ただし,なぜ例 6) のほうに括弧が無いのかは謎です.話を戻すと,例 5) は a5?
が設定済みのときに void 0
すなわち undefined
を返すようになっています.これに対して,例 4) と例 6) は,undefined
にはなりません.
まとめると,a ?= b
は,生成される JavaScript だけを見ると a = b unless a?
と同じになることがあるが,戻り値を考慮するなら a ? a = b
と同じ結果になるということです.
or= を調べてみる
こちらのほうも調べてみます.まず,CoffeeScript の例をつくって,
# conditional_assignments-3.coffeea1 or= b1 # 1)
a2 = a2 or b2 # 2)
a3 or a3 = b3 # 3)
c = (a4 or= b4) # 4)
c = (a5 = a5 or b5) # 5)
c = (a6 or a6 = b6) # 6)
コンパイルしてみます.
% coffee -bp conditional_assignments-3.coffee
var a2, a3, a5, a6, c;a1 || (a1 = b1);
a2 = a2 || b2;
a3 || (a3 = b3);
c = (a4 || (a4 = b4));
c = (a5 = a5 || b5);
c = a6 || (a6 = b6);
こちらも括弧の有無はあるものの,「a or= b
は a or a = b
」と言って差し支えありません.a or= b
では a
が真のときには代入が発生しないのに対し,a = a or b
では a
が真でも代入が発生するのが違います.
おわりに
CoffeeScript で便利な ?=
と or=
について調べてみました.コンパイル後の JavaScript に若干の違いがありますが,それぞれ,a ? a = b
と a or a = b
だと思っておいて間違いありません.a ?= b
と a = b unless a?
とは戻り値が異ります.a or= b
と a = a or b
は代入が発生するかどうかで違いがあります.以上,CoffeeScript 1.2.0 での話でした.
今回は Tumblr へのコメントで勉強させてもらいました.コメントをくださった id:murky-satyr に感謝します.