どひゃー JavaScript Scoping and Hoisting

ESLint さまさま。

以下が Treat var as Block Scoped (block-scoped-var) ルールで叱られる:

1         for (var i = 0; i < animetypes.length; ++i) {
2             animetypes[i] = animetypes[i].trim().toUpperCase();
3         }

こんな具合に:

 1   2394:18  error    'i' used outside of binding context            block-scoped-var
 2   2394:18  error    'i' used outside of binding context            block-scoped-var
 3   2394:18  error    'i' used outside of binding context            block-scoped-var
 4   2394:25  error    'i' used outside of binding context            block-scoped-var
 5   2394:25  error    'i' used outside of binding context            block-scoped-var
 6   2394:25  error    'i' used outside of binding context            block-scoped-var
 7   2394:50  error    'i' used outside of binding context            block-scoped-var
 8   2394:50  error    'i' used outside of binding context            block-scoped-var
 9   2394:50  error    'i' used outside of binding context            block-scoped-var
10   2395:24  error    'i' used outside of binding context            block-scoped-var
11   2395:24  error    'i' used outside of binding context            block-scoped-var
12   2395:24  error    'i' used outside of binding context            block-scoped-var
13   2395:40  error    'i' used outside of binding context            block-scoped-var
14   2395:40  error    'i' used outside of binding context            block-scoped-var
15   2395:40  error    'i' used outside of binding context            block-scoped-var

Treat var as Block Scoped (block-scoped-var) ルールに書かれてる NG 例だけみてても何言われてるかわからなかったが、「これみれ」に誘導されて JavaScript Scoping and Hoisting を読む。

げっ。 Only functions create a new scope. そうだったのか…。

「スクリプト言語のスコープ規則」はどの言語も何かしら悩ましい側面があって、Python のもなかなかヒドいが、javascript のこれは、なまじ「java」なんてのを冠してるもんだから「c 系言語と同じだろう」と思ってしまうのが罠よなぁ。

まず引用先で書いている通りで、「本当に c 系言語のようにブロックローカルをしたいんであれば」:

1         (function () {
2             for (var i = 0; i < animetypes.length; ++i) {
3                 animetypes[i] = animetypes[i].trim().toUpperCase();
4             }
5         }());

とすれば良い、ということになるのだが、今問題にしている「ループ記述」に限って言えば無論この解はバカげている。

なんとなく自分のコードのループ記述方法に統一感がなくて、書ける色んな書き方しちゃってるわけなんだけれど、ここをなんでインデクスループにしたのか、気分が思い出せない。あ、「中身を書き換えたい」からか。forEach でも書ける?:

1             animetypes.forEach(function (_, i) {
2                 animetypes[i] = animetypes[i].trim().toUpperCase();
3             });

なんか他の言語に慣れてると気持ち悪い気もするんだけれど、要素内容を変更したいならこういうことかなぁ。

こういうのはどうしてくれようか:

 1         var found = false;
 2         for (var i = 0; i < detail["gnr"].length; ++i) {
 3             if (genres.includes(detail["gnr"][i].toUpperCase())) {
 4                 found = true;
 5                 break;
 6             }
 7         }
 8         if (found !== genresincl) {
 9             return false;
10         }

うーん、ちょっと後回し。というかこういうシンプルなリニアサーチがなんか書きにくいんだよなぁ、javascript。なんか上手な書き方はないもんなんだろうか? (※末尾の追記参照。)


どうも上で書いたことも「めっちゃくちゃ FAQ」らしいんだけど、同じくループ関係で、やはり ESLint が検出してくれた「知りませんでしたぁ」があった。言うまでもなく(?) for-in の件。

なんか別の調べてたときに google 検索の一覧レベルで日本人がこれについて書いてるのを「一覧で」みた。まぁ「とてつもなく、凄まじく、とんでもなく」有名なんだろうね。まぁびっくりするわいさ。「頼んでない」ものがわんさか芋づるってくるんだから。(Python の dir の振る舞いに近いかもね。)

これも結局「やったね Good!」:

1 for (key in foo) {
2     if (Object.prototype.hasOwnProperty.call(foo, key)) {
3         doSomething(key);
4     }
5     if ({}.hasOwnProperty.call(foo, key)) {
6         doSomething(key);
7     }
8 }

と言うけれど、「だったら forEach とか別のもん使えや」ちぅハナシ。無論アタシは forEach に書き換えた。


ほんと「知らんことだらけだ」ということなんだけど、どうも ESLint のおかげで、一気に色々知ることが出来そうだ、ありがたい。


06:00追記:
なぜないと思い込んだ? find。ので:

1         var found = detail["gnr"].findIndex(function(e) {
2             return genres.includes(e.toUpperCase());
3         }) >= 0;

てだけじゃん、と。

書き忘れたけど「お行儀ツールの警告」は、こういう「より良い書き方への乗り換えを促す好機と考える」という使い方にこそ価値を見出すべき、てことを理解して使いなはれや。(こういうとこ、後輩や同僚を観察してて「ヲィ」と思うのって。)なんてことを「万年 javascript 初心者の癖に」えらそーに口走ってみる。