◆ 関数の中に関数いっぱいかくより スコープ内で並列に書いて一つだけ外に出す

ちょっと大きめなことをやる時でこんな書き方になることがよくあります
function fn(arg, arg2){
    var a = arg.split("\n").map(e => e.split(","))
    if(!Array.isArray(arg2)){
        arg2 = [arg2]
    }
    return p(a, arg2)

    function p(){

    }

    function q(){

    }

    function r(){

    }

    function s(){

    }
}

例えば長文のテキストをパースして目的の構造に変換するとか

手続き的に関数の中にダラダラとやることを順に書いていくと見づらくなるので 適当にやることで関数を作って 相互に呼び出したり再帰したりで目的の形にしてます

この例だと arg, arg2 の引数があって引数のフォーマットを整えたあとに p を実行してそのまま return しています
省略してますが p が q や r を呼び出したりします

p とか q とかはこの関数内部でしか使えないような ここ固有の処理なので外には出さないです
出してしまうと関数がいっぱいでごちゃごちゃしてきます

汎用的で別のところにもつかえるならともかくそこ専用の処理するものがまざると見づらいですよね
なんでもかんでもグローバルに置かない というのと一緒です


なのでとりあえずこうしているのですが なんか嫌なところもあります

ひとつめ

ひとつめは 毎回関数定義されるところ
p とか q とかです
10 個近くあるとして fn を呼び出すために毎回定義するのはムダな感じがすごくします

アロー関数などで map や forEach の数だけ無名関数があったりして 毎回作られる関数なんて山のようにあるんだから パフォーマンス的には気にするほどでもないともいえます
それでも できるだけなくしたいなぁ って感じはするんですよね

ふたつめ

ふたつめは arg や arg2 のような引数や p を呼び出す前に fn のスコープで宣言した変数が p や q などの関数全体で参照できること

外のスコープ見れるのが当たり前なのですが いわゆるグローバルスコープみたいなところを見てる気分になってなんか気持ち悪いです

arg2 は直接参照できてしまうから p に引数として渡さないで直接みればいいや という感じになってしまいそうになります
でもそうすると あとあと困ることがあったりもするんですよね

外スコープのもの見るより引数として受け取ったものだけを見るようにしていたほうが 変更や使い回しに強いと思います

こうしてみた

なので 最初の引数を参照できないようにしてしまいつつ 1 つ目の関数を毎回ムダに宣言するのをなくすためにこうしてみます
const fn = function(){
    return function(arg, arg2){
        var a = arg.split("\n").map(e => e.split(","))
        if(!Array.isArray(arg2)){
            arg2 = [arg2]
        }
        return p(a, arg2)
    }

    function p(){

    }

    function q(){

    }

    function r(){

    }

    function s(){

    }
}()

全部の関数を並列にならべてエントリポイントになる関数を return して外側に出します

p とか q は最初に 1 度宣言されるだけですし arg や arg2 を p などから参照できなくなっています


気になってたところは解決して問題なしのようにみえるのですが やっぱりデメリットもあります

関数の即時実行をするので代入形式になります
function 構文と違ってスコープの最初で宣言してくれません
代入式が実行されるまでは関数が使用できません

上の方の例にあるように メインの処理を先に書いて return 文を書いた後に function をまとめておく書き方ができなくなります
関数定義のあとにメインの処理より メインの処理のあとに関数定義のほうが見やすいと思うので 常に下のほうが良いとも言えないんですよね