はい、昨日の続き。
## Ch.4 Expressions and Operators
### 4.4.1 Conditional Property Access
ES2020 で以下の2つのプロパティアクセス式が追加された:
expression ?. identifier expression ?. [ expression ]
普通のプロパティアクセスの場合には、左側の式が null
か undefined
だと TypeError が出る。 ?.
や ?.[]
を使えばエラーは出ずに undefined
が返される。
Welcome to Node.js v14.2.0. Type ".help" for more information. > let a; undefined > a.b Uncaught TypeError: Cannot read property 'b' of undefined > a?.b undefined > a = null null > a.b Uncaught TypeError: Cannot read property 'b' of null > a?.b undefined
面白いな。そして "short-circuiting" なので ?.
の左側が null
または undefined
の場合はその右側の式は実行されない。例えばこんな感じ:
> let index = 0 undefined > a[index++] Uncaught TypeError: Cannot read property '0' of null > index 1 > a?.[index++] undefined > index 1
?.
を使ったときは index
はインクリメントされてない。
### 4.5.1 Conditional Invocation
Conditional Property Accessの関数呼び出し版。これもES2020。
expression ?.()
左側が null
または undefined
の場合は undefined
を返す。ふむふむ。
function square(x, log) { log?.(x); return x * x; }
### 4.8 Arithmetic Expressions
数値に変換できないオペランドは NaN
になって、どちらかのオペランドが NaN
の場合は、結果はほぼ全て NaN
になる。「ほぼ全て」って書いてるから NaN
にならないケースがあるんかな。ま、気にしなくていいか。
> let a; undefined > a + 1 NaN > a / 1 NaN > 1 / a NaN > 0 / a NaN
### Exponentiation Operator (**
)
- ES2016で追加された
- 以前から存在する
Math.pow()
と同じ - 右から左に適用されることに注意。
2**2**3
は2**8
になる。4**3
ではない。
> 2**2**3 256 > 2**(2**3) 256 > (2**2)**3 64
- Unary operatorが直前にある場合は、カッコをつけないと
SyntaxError
になる。適用順序の曖昧さ回避のため。親切ね。
> -3**2 -3**2 ^^^^ Uncaught: SyntaxError: Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence > -(3**2) -9 > (-3)**2 9
### Division Operator (/
)
あぁ、これは Java と違うな。JavaScript では全部の数値が floating-point だから、整数と整数の割り算をしても小数になる。Java だと 5 / 2 = 2
になるけど、JavaScript だと 2.5
になる。
Java の場合:
| Welcome to JShell -- Version 14.0.1 | For an introduction type: /help intro jshell> 5 / 2 $1 ==> 2
JavaScript の場合:
> 5 / 2 2.5
### 4.8.1 The + Operator
こういうの難しいなぁ。面白いけど。
まず簡単なケース:
- どちらも数値なら計算される
- どちらも文字列なら連結される
> 1 + 2 3 > "Hello" + "World" 'HelloWorld'
数値と文字列が混ざっているときは文字列連結が優先される:
> "1" + 2 '12' > 1 + "2" '12' > NaN + "2" 'NaN2'
オブジェクトの場合はプリミティブ変換が実行される:
- Dateオブジェクトは
toString()
で変換される - それ以外のオブジェクトは
valueOf()
がプリミティブを返すならその値が使用される - 多くのオブジェクトは有効な
valueOf()
を持たないのでtoString()
で変換される
// Dateオブジェクト > new Date() + 12345 'Mon May 18 2020 20:58:44 GMT+0900 (Japan Standard Time)12345' // オブジェクト > 1 + {} '1[object Object]' // valueOf() が文字列を返す > 1 + {valueOf(){return "aaa"}, toString(){return "bbb"}} '1aaa' // valueOf() を独自定義してない > 1 + {toString(){return "bbb"}} '1bbb' // valueOf() がプリミティブを返さない > 1 + {valueOf(){return {}}, toString(){return "bbb"}} '1bbb' // valueOf() が数値を返す > 1 + {valueOf(){return 123}, toString(){return "bbb"}} 124
- 片方が文字列の場合は、もう片方も文字列に変換されて連結される
null
は'null'
に、undefined
は'undefined'
に変換される
- それ以外の場合は、両方とも数値(または
NaN
)に変換されて加算されるnull
は0
に、undefined
はNaN
に変換される
> 2 + null 2 > "2" + null '2null' > 2 + undefined NaN > "2" + undefined '2undefined' > true + false 1 > true + NaN NaN
面白い。
### 4.9.1 Equality and Inequality Operators
これは有名なやつ:
==
や!=
は型変換が実行されるから予期せぬ結果になってしまうので使わない。===
と!==
を使う。
### 4.9.2 Comparison Operators
型が混ざった比較もやりたくないなー。
### 4.9.3 The in Operator
オブジェクトがプロパティをもっているかどうかをチェックする:
> let point = {x: 1, y: 1} undefined > "x" in point true > "z" in point false > "toString" in point true > let data = ["a", "b", "c"] undefined > "0" in data true > 1 in data true > 3 in data false
使うときあるのかな?
### 4.10.2 Logical OR (||
)
なるほど。"falsy" な値の場合は false
扱いになって右側の式が評価されるから、定義されていない場合の値を指定するときに使われるのかー。
let max = maxWidth || preferences.maxWidth || 500;
- falsy な値を有効な値として扱いたい場合には考慮が必要。例えば
maxWIdth
にゼロを指定したい場合など。この場合は??
を考えてみる。 - ES6より前のコードでは引数のデフォルト値の指定によく使われてたらしいけど、ES6以降は引数にデフォルトが指定できるようになったのでそちらを使えばいいとのこと。
### 4.13.2 First-Defined (??
)
- ES2020
- 左側のオペランドが
null
でもundefined
でもない場合に、その値を返す。それ以外の場合は右側のオペランドの値を返す。 &&
や||
と同様に??
も short-circuiting
let max = maxWidth ?? preferences.maxWidth ?? 500;
こっちの方が ||
より好きだな。
### 4.13.3 The typeof Operator
へー。 typeof null
は 'object'
なのかー。
> typeof null 'object' > typeof undefined 'undefined'
今日はこれくらいでおなかいっぱい。