JavaScriptの勉強中:その2 Expressions and Operators

はい、昨日の続き。

bufferings.hatenablog.com

## Ch.4 Expressions and Operators

### 4.4.1 Conditional Property Access

ES2020 で以下の2つのプロパティアクセス式が追加された:

expression ?. identifier
expression ?. [ expression ]

普通のプロパティアクセスの場合には、左側の式が nullundefined だと 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**32**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 )に変換されて加算される
    • null0 に、 undefinedNaN に変換される
> 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'

今日はこれくらいでおなかいっぱい。