parseIntを小数点切り捨てに使わない

TwitterのTL上でにわかに話題になっていた。parseIntは引数をstringにしてからint変換するので、小数点以下の切り捨てには使わん方が良いというお話。

> parseInt(0.000001)
0
> parseInt(0.0000001)
1

これは以下のように処理されているのと同じ

> (0.000001).toString();
'0.000001'
> parseInt('0.000001');
0
> (0.0000001).toString();
'1e-7'
> parseInt('1e-7');
1

ES2015対応のブラウザやnode v4系以降ならば、代わりに Math.trunc を使うと良いと思います。

元来 parsetInt は文字列を解析して整数を返す関数で、小数点以下を切り捨てる関数ではありません。

プログラミングの世界でparseという言葉は、主に文字列を解析して何かをする機能に対して使われる単語です。関数の正確な仕様を把握していなかったとしても(もちろん把握している方が良いのですが)、parseホゲホゲという関数に数値を渡すのは間違ってるのでは?と判断できる感覚を身に付けてしまうと良いと思います。

クロージャとbindの引数束縛パフォーマンス

JavaScriptで引数を束縛したい場合、クロージャかbindを使うのだが…

function plus(x, y) {
    return x + y;
}

function multiply(x, y) {
    return x * y;
}

// Function.prototype.bindで束縛
const plus1 = plus.bind(null, 1);

plus1(5);    // => 6


// closureで束縛
function closure(func, param) {
    return function(index) {
        return func(param, index);
    };
}

const twice = closure(multiply, 2);

twice(5);    // => 10

なんとなく、どちらが速いのか気になったので比べてみた。

console.time('bind');
for (let i = 0; i < 100000; i++) {
    const plusX = plus.bind(null, i);
    const multiplyX = multiply.bind(null, i);

    for (let j = 0; j < 10; j++) {
        plusX(j) + multiplyX(j);
    }
}
console.timeEnd('bind');


console.time('closure');
for (let i = 0; i < 100000; i++) {
    const plusX = closure(plus, i);
    const multiplyX = closure(multiply, i);

    for (let j = 0; j < 10; j++) {
        plusX(j) + multiplyX(j);
    }
}
console.timeEnd('closure');

結果

$ nvm use v6
Now using node versions/node/v6.2.1

$ node speed.js
bind: 106.394ms
closure: 33.421ms

node v6.2.1ではクロージャを使うほうが3倍ほど高速らしい。

node ver.4でも測定

$ nvm use v4
Now using node versions/node/v4.4.5

$ node speed.js
bind: 949ms
closure: 33ms

node ver.4では30倍程度の差がついた。というよりも、ver.4とver.6でFunction.prototype.bindが10倍も高速になっている。

node v4.4.5にはV8 v4.5、node v6.2.1にはV8 v5.0が搭載されている。メジャーバージョンアップで大きな最適化が施されていることがわかる。さすがGoogleである。未だJSエンジンの進化は止まらず。

なおこのコードをFirefoxChromeの最新版で比較してみると…

Chrome 51.0.2704.79

bind: 127.187ms
closure: 34.819ms

Firefox 47.0

bind: タイマー開始
bind: 36.4ms
closure: タイマー開始
closure: 6.85ms

Firefox圧勝!