javascript: 続・リンターな max statements (とか complexity) との格闘と IIFE との愉快な関係

なんとなく「誤魔化すため」というノリがよろしくないんだけれど。

一つ前のを現実にあちこちで適用してみて気付いたことが二つあったので、一応書いておこうかと。


一つ目はネガティブな話。「絶対適用出来ないパターンがある」。ワタシのそのまんまの例でこれ:

 1         while ((m = new RegExp("<" + tag).exec(ls))) {
 2             ls = ls.slice(m.index);
 3             let lsr = new RegExp(endtag, "g");
 4             let lsm = lsr.exec(ls);
 5             let part = ls.slice(0, lsm.index + endtag.length);
 6             let cnt = part.match(new RegExp("<" + tag, "g")).length;
 7             while (part.match(new RegExp(endtag, "g")).length < cnt) {
 8                 lsm = lsr.exec(ls);
 9                 part = ls.slice(0, lsm.index + endtag.length);
10                 cnt = part.match(new RegExp("<" + tag, "g")).length;
11             }
12             let pushing = fn(part);
13             if (pushing) {
14                 result.push(pushing);
15             }
16             ls = ls.slice(lsm.index + endtag.length);
17         }

これ、ここだけで max-statements (デフォルト10)に引っかかってしまうので IIFE してしまいたくなるところだけれど、たとえば:

 1         while ((m = new RegExp("<" + tag).exec(ls))) {
 2             ls = ls.slice(m.index);
 3             let lsr = new RegExp(endtag, "g");
 4             let lsm = lsr.exec(ls);
 5             let part = ls.slice(0, lsm.index + endtag.length);
 6             (function () {
 7                 let cnt = part.match(new RegExp("<" + tag, "g")).length;
 8                 while (part.match(new RegExp(endtag, "g")).length < cnt) {
 9                     lsm = lsr.exec(ls);
10                     part = ls.slice(0, lsm.index + endtag.length);
11                     cnt = part.match(new RegExp("<" + tag, "g")).length;
12                 }
13             }());
14             let pushing = fn(part);
15             if (pushing) {
16                 result.push(pushing);
17             }
18             ls = ls.slice(lsm.index + endtag.length);
19         }

とすれば(当然) no-loop-func のほうで NG になる。(no-loop-func の話は前にした。)


2つめの話は、「リンターを黙らせる」という目的からよりも「これは意図も明白になるし、えんではないか」と思うパターンがあってな。つまり「戻りを持つタイプの IIFE」、これ、なかなかええもんかもしらん。もとがこうだとして:

 1     let _test;
 2     if ($("#node-finder-name-regexp").is(":checked")) {
 3         find_what = new RegExp(find_what, "i");
 4         _test = function (s) {
 5             var m = find_what.exec(s);
 6             return (m !== null);
 7         };
 8     } else {
 9         _test = function (s) {
10             return (s.toLowerCase().indexOf(find_what) >= 0);
11         };
12     }

こう書き換えるということ:

 1     let _test = (function () {
 2         if ($("#node-finder-name-regexp").is(":checked")) {
 3             find_what = new RegExp(find_what, "i");
 4             return function (s) {
 5                 let m = find_what.exec(s);
 6                 return (m !== null);
 7             };
 8         }
 9         return function (s) {
10             return (s.toLowerCase().indexOf(find_what) >= 0);
11         };
12     }());

何が嬉しいかって、「未初期化で変数定義(宣言)だけする」ことも同時に避けることが出来てるのよね。昔々の C 言語で「でろでろ書くのが大好きな人」にはわからんだろうが、たとえばこんな C はやなのだ:

1 double x, y, z;
2 int i, j, k, l, m, n;
3 // ... とてつもなく長大なステートメンツ
4 for (i = 0; i < ...; ...) {
5 }

てのに通じる。「C であろうと局所化に注意を払っているプログラマ」と「C++ でより是とされる、「出来るだけ宣言は遅延」」なノリからは、「明な初期化を伴わない変数定義」が滅茶苦茶気持ち悪いのよね。(無論ステートメント数を稼ぐことそのものも気持ち悪い。)

そして、「だからといって名前付きの関数にするほどのもんではなかろ」のために「IIFE で書くと嬉しいのである」てことね。もとの動機は「リンターを黙らせる」という不純なものだったけれど、結果的には「望ましいスタイル」になった、の巻。