読者です 読者をやめる 読者になる 読者になる

JavaScriptの型変換に今更刺し殺される

ある日デバッガを見ていたらこんな文字列が目に入った

Mon Feb 13 2017 18:55:53 GMT+0900 (JST)1000

ナニコレ…?

ソース見たらこんな風になっていた

var now = new Date();
var startTime = now - 1000;
var endTime = now + 1000;

Dateオブジェクトに数値を足し引きしようとしている?!

ていうか、このコード、よく今まで問題なく動いてたなwww

> new Date() + 1000
'Mon Feb 13 2017 19:02:18 GMT+0900 (JST)1000'
> new Date() - 1000
1486980143743

Dateに引き算すると数値型に変換されてエポックになる。これは意図通りだが、足し算すると文字列型に変換されるわけですか、足そうとしているのが数値でもか。であれば

> +new Date() + 1000
1486980570179

ついでなので他のオブジェクトも見てみますかw

> [] + 1000
'1000'
> [] - 1000
-1000
> [1,2,3] + 1000
'1,2,31000'
> [1,2,3] - 1000
NaN

空の配列を数値に型変換すると 0 、文字列に型変換すると空文字列。空でない配列を数値に型変換するとNaNになるあたり勘弁してほしい。

> {} + 1000
1000
> {} - 1000
-1000

ん?

> new Object() - 1000
NaN
> new Object() + 1000
'[object Object]1000'

んん?

もしかして {} はオブジェクトではなくブロックとして扱われているのか? ならば

> ({}) - 1000
NaN
> ({}) + 1000
'[object Object]1000'

もうやだこれwww

> Object.create({}, {toString: {value: function(){ return 'くそ' }}}) + 1000
'くそ1000'

こういう挙動を個別に覚えなくてもいいようなコーディングを心がけたいところ。だーけーど、それならば静的型付け言語の方がいいのでは…、という話になっちゃうんすよね。

mongodb 2系と3系でpropertyの並び順が変わった

mongodbを2.4から3.2にアップグレードする作業を行っている。3.2にするとテストが大量に失敗するようになったのだが、その原因の一つがこれ。

2.4

> db.Test.update({_id: "nyan"}, {$set: {hoge: {b: {x: 1}}}}, {upsert: true});
> db.Test.findAndModify({query: {_id: "nyan"}, update: {$set: {'hoge.a': {x: 1}}}, new: true});
{
    "_id" : "nyan",
    "hoge" : {
        "a" : {
            "x" : 1
        },
        "b" : {
            "x" : 1
        }
    }
}

3.2

mongos> db.Test.update({_id: "nyan"}, {$set: {hoge: {b: {x: 1}}}}, {upsert: true});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongos> db.Test.findAndModify({query: {_id: "nyan"}, update: {$set: {'hoge.a': {x: 1}}}, new: true});
{
    "_id" : "nyan",
    "hoge" : {
        "b" : {
            "x" : 1
        },
        "a" : {
            "x" : 1
        }
    }
}

2.4はsetした順番に関わらずa→bとなるが、3.2はsetした順番に並んでいる。アプリケーションコードでオブジェクトを配列に変換しており、その配列要素の順番が変わったためテストが失敗するようになった。

mongo的にもJS的にもプロパティの順番は仕様で決まっていないので順番に依存すべきではない。今回はアプリ側では配列要素の位置が入れ替わっても問題なく、テストコードだけが順番に依存していたのでテストを直すだけにした。