Twitterでふぁぼったものをひたすら試します

"Thanks for inventing javascript" を解読してみた

JavaScriptの気持ちになってみた
時々Swiftの話が出てくるのは気分の問題
JavaScript書いたことないので解釈間違えてそう

typeof NaN

typeof NaN

'number'

これは別に普通ではなかろうか。そうでもないのか??いやまあ名前に反してるかもしれないが。

NaN - Wikipedia

個人的にはこっちのほうが微妙↓↓

typeof typeof NaN

'string'

いいのか文字列で。いいのか。はい。
Swiftはmetatype typeで返す。

type(of: type(of: Float.nan))

Float.Type

9999999999999999

9999999999999999

10000000000000000

JavaScriptのNumberは実は64bit float。

符号部 指数部 仮数
0 10000110100 0001110000110111100100110111111000001000000000000000

これを10進数に戻すと 10000000000000000 になってしまう。

0.5 + 0.1 == 0.6 / 0.1 + 0.2 == 0.3

0.5 + 0.1 == 0.6

true

符号部 指数部 仮数
0.5 0 01111111110 0000000000000000000000000000000000000000000000000000
0.1 0 01111111011 1001100110011001100110011001100110011001100110011010
0.5+0.1 0 01111111110 0011001100110011001100110011001100110011001100110011
0.6 0 01111111110 0011001100110011001100110011001100110011001100110011
0.1 + 0.2 == 0.3

false

符号部 指数部 仮数
0.1 0 01111111011 1001100110011001100110011001100110011001100110011010
0.2 0 01111111100 1001100110011001100110011001100110011001100110011010
0.1+0.2 0 01111111101 0011001100110011001100110011001100110011001100110100
0.3 0 01111111101 0011001100110011001100110011001100110011001100110011

浮動小数点数の宿命。

Math.max() / Math.min()

Math.max()

-Infinity

Math.min()

Infinity

仕様です。

各静的メソッドの構文は次のようになっている。

Math.max([value1[,value2[, ...]]])
Math.min([value1[,value2[, ...]]])

Swiftの max(_:_:)min(_:_:) は1つ以上の引数を要するのでそもそもこんなことはできない。

加算演算子

加算演算子の処理方法はちゃんと仕様で決まっている。

以下意訳

  1. lref を左辺の評価結果とする。
  2. lvallref の参照外しの結果とする。
  3. rref を右辺の評価結果とする。
  4. rvalrref の参照外しの結果とする。
  5. lprimlval をプリミティブ化したものとする。
  6. rprimrval をプリミティブ化したものとする。
  7. lprimrprim のどちらかがStringならそれぞれをStringにして結合結果を返す。そうでなければNumberにして和を返す。

[] + [] / [] + {} / {} + []

[] + []

''

[] + {}

'[object Object]'

{} + []

0

配列の足し算は未定義っぽい。むずかしい。

'[object Object]' については ''[object Object] を足してみたのだろうなあと推測できなくはないが、 0 は厳しい。 0 + NaNNaN になるならまだわかる。

ちなみに、

{} + {}

'[object Object][object Object]'

let a = []
let o = {}
a + o

'[object Object]'

[] + {} === {} + []

true

等々となるので、 {} + []'[object Object]' を返してくれさえすれば未定義といえど辻褄が合っているように見える。気がする。

(!+[]+[]+![]).length

(!+[]+[]+![]).length

9

.length はここでは関係ない。

! + []

true

あの…なんで ! + [] が成立するんでしょうか…。 [] + !! が引数(?)を要求するのでさすがにダメ。

! + [] はさておき、 ! + [] + []true + []'true' + '''true'と考えればそれっぽい。

【追記ここから】
そういえば単項演算子+ とかいうのいましたね…

  1. expr を左辺を評価したものとする
  2. Numberに変換したものを返す

なので、 ! + []! (+ [])!0true

ご指摘ありがとうございました。
【ここまで】

論理否定演算子

  1. expr を右辺を評価したものとする。
  2. oldValueexpr をBooleanに変換したものとする。
  3. oldValuetrue なら falsefalse なら true

という処理をするので、 ![]![]!truefalse となるっぽい。じゃあなんで ! + []true なのかと聞かれるとやはりわからない。

【追記ここから】
[]true であり 0 であり、 0false 。なるほど。
【ここまで】

9+"1"

9+"1"

"91"

"1" が文字列なので文字列結合が実行される。

91 - "1"

91 - "1"

90

減算演算子の場合は lprimrprim の型にかかわらずNumberに変換した上で四則演算が実行される。

"a" などが含まれている場合、 NaN に変換されるので減算結果も NaN になる。

等価演算子

仕様上、

== の判断は

  1. 両辺が同じ型なら === の結果次第。
  2. 両辺がどちらも nullundefined だったら true
  3. NumberとStringの比較の場合はStringの方をNumberに変換したうえで == で比較。
  4. Booleanの場合はそれをNumberに変換した上で == で比較。
  5. 両辺のどちらか一方がObjectだった場合はそちらをプリミティブ化したうえで == で比較。
  6. 以上のどれにも当てはまらない場合は false

=== の処理は

  1. 両辺の型が違うなら false
  2. Numberのとき、どちらかが NaN なら false 。同じ値なら true-0+0 は同じ値とみなす)。どれでもないなら false
  3. Number以外のときは型の種類ごとに両辺が同じかどうかを判断。

となっている。

true + true + true === 3

true + true + true === 3

true

true はStringではないのでNumberに変換した上で加算する。 true1 扱い。
両辺ともに同じ数となるので結果は true

true == 1 / true === 1

true == 1

true

true == 11 == 11 === 1 と経て最後は true

true === 1

false

両辺の型が違うので false

[] == 0

[] == 0

true

空の配列 [] をプリミティブ化した時に 0 になるので 0 === 0true
ちなみに空文字列で "" == 0 した場合も true

けっきょくよくわからなかったこと

  • ! + [] がなぜOKなのか 【追記】解決した
    • ! はAdditiveExpressionということでいいんですかね
  • [] == 0true[""] == 0true["a"] == 0false なのはなぜか
    • 空文字列と同様に空配列はfalseな感じがあるらしいが、それがどこに書いてあるのか仕様が長すぎてわからない
      • 【追記】 []true00falseobjecttrue というのが原因だろうが、ここまで来るともはや何が何だかわからない。
  • Swiftで [] + [:] とやると[(key: AnyHashable, value: Any)]型になるのだが、この + はどこからきたのか

むずかしい!

参考文献

typeof

浮動小数点数ツール

max / min

加算演算子

等価演算子