すこしふしぎ.

VR/HI系院生による技術ブログ.まったりいきましょ.(友人ズとブログリレー中.さぼったら焼肉おごらなきゃいけない)

【coffeescript】存在確認演算子【?】

こんばんは.1000chです. 先週末,同期内定者とのチーム開発が一段落しました. 内定式でそれの発表など行いましたが,昨年のインターンに続き非常に良い経験でした. 気が向いたらそのうち記事にまとめます.

この開発のなかで自分は主にフロントを扱っており,実はその影響から最近coffeescriptの記事が多いかったりしてます. 今回もその流れでcoffeescriptネタを1ついきますね.

存在確認演算子

coffeescriptの機能の中で,個人的に「慣れたら便利なんだけどいまいち使い方よくわからんな」と思っていたのが,存在確認識別子 の 「?」です. そもそものjavascriptには無い機能なので,慣れるまで取っ付きにくいものかと思います. (どうやらrubyにおける?に似ているようですが)

一言で言うと「そのプロパティが存在するかどうか」を判断した上で,「続く処理を行うかどうか」を選択できる機能というところですが... まぁわかりにくいですね.実際に見ていくのが早そうです.

javascriptにおける?

まずjavascriptにおける?といえば

var result = flag ? "flag on" : "flag off";

みたいな感じで,いわゆる三項演算子として使いますよね.

しかし,cofffescriptにおいてはこの目的で?を使うことはできません. 三項演算子的な表現をする場合は,

# flagには適当なbool値を与える
result = if flag then "flag on" else "flag off" 
// コンパイル後
var result;
result = flag ? "flag on" : "flag off";

というように,if文を1行で書く形で実現します. より自然言語としての英語に近い表現ですね.

coffeescriptにおける?

さて,ではいよいよcoffeescript風に?を使ってみましょう. 参考はこちら.

The Little Book on CoffeeScript - Syntax

いわく,

CoffeeScriptの存在確認演算子である?は変数がnullでないか、undefinedでなければtrueを返します。

とのことです. 実際に触ってみましょう.

# null,undefined は false
console.log null? #false
console.log undefined? #false

# 未定義変数もfalse
console.log undefined_variable? #false

# その他はtrue
console.log 0? #true
console.log false? #true
console.log ""? #true

# 二重につけると..
console.log null?? #true (false?と等しい.使い道ないかも)

null,undefinedの場合は変数が存在しないとしてfalseが返りますが,その他falseと判断されるような変数(ex.false,0)に関しては「オブジェクト自体は存在する」為,trueが返ってくるようですね.これが,存在確認演算子と呼ばれる所以でしょう.

もう一つ,オブジェクトに対して利用した例を見てみましょう.

#オブジェクトに対する利用
myObj = 
    a: "prop_a"
    b:
        b1: "prop_b1"
        b2: "prop_b2"

console.log myObj? #true
console.log myObj.a? #true
console.log myObj.b? #true
console.log myObj.c? #false
console.log myObj.b.b1? #true
console.log myObj.b.b2? #true
#console.log myObj.c.b2? #TypeError: Cannot read property 'b2' of undefined
console.log myObj.c?.b #undefined

ここで注目すべきは,存在しないプロパティc周りの処理です.通常myObj.c.bというよう処理を書いてしまうと,undefined.bと同値になり,TypeErrorの例外が発生してしまいます.

そこでmyObj.c?.bと記述すると,myObj.cundefinedと判定された時点で,undefinedを返してくれるようになります.コレを応用すると取得したデータを扱う際に,プロパティの存在確認をしつつ,存在するときのみ値を返す,という記述もできます.

console.log myObj.b?.b1 #prop_b1
console.log myObj.c?.b1 #undefined

同様に,メソッドを持つときのみ実行,ということも可能です.

論理演算子としての?

ここが自分が「?ってよくわからんな」と思ったポイントでもあるのですが,coffeescriptはにおける?は論理演算子としても利用することができます.実例を見てみましょう.

console.log 2 || 3 # 2
console.log 2 or 3 # 2
console.log 2 ? 3  # 2

console.log null || 3 #3
console.log null or 3 #3
console.log null ? 3  #3

ただし注意が必要なのは,存在確認演算子に置いては,0,'',falseなどはtrueと評価される点です.

console.log false || 3 #3
console.log false or 3 #3
console.log false ? 3  #false

console.log 0 || 3 #3
console.log 0 or 3 #3
console.log 0 ? 3  #0

改めて確認しておくと,?においてfalseと評価されるのは nullおよびundefinedのみです.

拡張論理演算子としての?=

coffeescriptでは論理演算子の拡張としてvar1 ||= var2という表現を利用できます.var1を評価し,falseとなったときのみ,var1にvar2を代入することができます.

a = 0
a ||= 1 # a or= 1 ともかける
console.log a #1

コンパイル

var a;
a = 0;
a || (a = 1);
console.log(a);

しかしこれでは,aがfalseと評価される際に常に書き換えられてしまいます. 例えば「オブジェクトにプロパティが存在しない時のみデフォルト値をセット」という表現がやりづらいです. つまり,プロパティとして0や空文字を持つ場合もデフォルト値に書き換えてしまうのです.

そこで利用できるのが,?=という表記です. 流れ的にお分かりかと思いますが,存在確認演算子によってfalseと判定される場合のみ代入することが可能となります.

# ?= のデモ
obj = 
    a: 0
    b:
        b1:1
        b2:2

console.log obj
# { a: 0, b: { b1: 1, b2: 2 } }

obj.a ?= 1 # obj.a は既に存在するので書き換えられない
obj.c ?= 3 # obj.c は存在しないので,新規プロパティの追加

console.log obj
# { a: 0, b: { b1: 1, b2: 2 }, c: 3 }

まとめ

coffeescriptにおける存在確認演算子?の使い方をまとめました.

  • var?varnull,undefinedの時のみfalseを,ソレ以外はtrueを返す
  • obj.a?.bでaが存在しないときもTypeErrorを回避できる
  • obj.method?()メソッドがあるときのみ実行できる
  • ?||,or同様論理演算子としても利用できる
  • ?=は拡張論理演算子として代入に利用できる