JavaScript-保守コード作成、関数プログラミングと純関数


JavaScript-保守コード作成、関数プログラミングと純関数
JavaScriptは、関数式プログラミングとオブジェクト指向プログラミングのハイブリッドプログラミング言語であり、自身のいくつかの拡張可能性(例えば、関数パラメータの数とタイプの不確定)を加えて、JavaScriptが非常に柔軟であり、もちろん、非常に制御できないとも言えます.この特徴は、チームが共通の先端プロジェクトを維持する時、JavaScriptコードは非常に読みにくいかもしれません.考えてみてください.新しいチームに参加して、他の人のコード自体を読んでもらうのは簡単ではないです.チームの注釈習慣とプログラミングのスタイルがあまりよくないと、jsファイルごとに何をしているのか分かりにくいです.そのため、メンテナンスできる高品質コードを作ることは本当に重要です.
1.問題の出所
コードを読むのは文章を読むより難しいのは、コードの一部が多くの関数に依存する可能性があります.各関数の役割を知るために他のコードブロックにジャンプしなければなりません.これは私たちの読書習慣に合わないです.ある本の中で、「対象言語に向けての問題は、いつまでも隠し環境を身につけていることです.バナナ一つだけが必要ですが、バナナを持っているゴリラと、森全体が得られます.」したがって、私たちのコードの中でJavaScriptの特徴をより巧みに利用して、関数を使ってプログラミングしてみてください.JSコードの維持性が高く、読み取り可能性がもっと強いです.
2.純関数を使ってJSコードのメンテナンス性を高める
まず、関数式プログラミングとは何かを調べてみます.
関数式プログラミング言語では、関数は最初のクラスのオブジェクト、すなわち、関数は他のオブジェクトに依存せずに独立して存在することができますが、オブジェクトに向かう言語では、関数(方法)はオブジェクトに依存し、オブジェクトの一部に属します.この点は関数式言語における関数のいくつかの特別な性質を決定しており、例えば、伝達/着信パラメータとして、普通の変数として作用する.
JavaScriptの関数は「一等公民」という言い方をよく聞きます.関数が「一等公民」と言われた時、実際にはそれらと他のオブジェクトは同じです.他のどのタイプのデータに対しても同じようにそれらを扱うことができます.それらは配列に存在し、パラメータとして伝達し、変数に与えられます.関数プログラミングの基本は純粋な関数です.
純関数は同じ入力でいつまでも同じ出力を得る関数であり,観察可能な副作用は全くない.
2.1純関数の理解
実際には、純粋な関数は、外部環境(例えば、グローバル変数、DOM)に依存せず、外部環境を変化させない(例えば、要求を送信し、DOM構造を変更する)関数の出力は、関数の入力によって完全に決定される関数であることを理解することができる.例えば、sliceとspliceは、この二つの関数の役割は同じです.しかし、それぞれの方式は大きく違っています.sliceが純関数の定義に適合するというのは、同じ入力に対して同じ出力を返すことが保証されているからです.しかし、spliceはその配列を粉砕して呼び出して、また吐き出します.これは観測可能な副作用,すなわちこの配列が永久に変化することを生じる.
関連コードはgithubを確認してください.
        var array1 = [0,1,2,3,4,5,6];
        var array2 = [0,1,2,3,4,5,6];

        var spliceArray = array1.splice(0,2);
        var sliceArray = array2.slice(0,2);

        console.log('array1: ' + array1);
        console.log('spliceArray: ' + spliceArray);

        console.log('array2: ' + array2);
        console.log('sliceArray: ' + sliceArray); 
実行結果
array1: 2,3,4,5,6
spliceArray: 0,1
array2: 0,1,2,3,4,5,6
sliceArray: 0,1
spliceは元の配列を変えましたが、sliceはありません.sliceは元の配列を変えないほうが「安全」だと思います.元のグループ数を変えるのは「副作用」です.
2.2非純関数がもたらす可能性のある「副作用」
「副作用」を詳しく調べて理解を深めましょう.じゃ、純粋な関数の定義で言及した非常に邪悪な副作用は何ですか?
副作用は,計算結果の過程において,システム状態の変化,あるいは外部世界との観測可能な相互作用である.
副作用が含まれていますが、これに限られません.
  • リスト内容
  • ファイルシステムを変更する
  • レコードをデータベースに挿入する
  • http要求を送信します.
  • 可変データ
  • プリント/ロゴ取得ユーザ入力
  • DOMクエリ
  • は、システム状態というリストにアクセスしても書き続けることができます.概括的に言えば、関数の外部環境との相互作用はすべて副作用です.この点は副作用なしのプログラムの実現可能性を疑わせるかもしれません.関数式プログラミングの哲学は、副作用が不正行為の主な原因であると仮定している.
  •         function getName(obj){
                return obj.name;
            }
            function getAge(obj){
                return obj.age;
            }
            function selfIntroduction(people){
                console.log(getName(people));
                console.log(getAge(people));
            }
    
            var Lee = {
                name: 'LYY',
                age: 25
            };
    
            selfIntroduction(Lee);
    実行結果
    LYY
    25
    明らかにselfIntroductionは、この関数は純関数ではなく、getNamegetAgeの2つの関数に依存しています.もし私が誤って関数の機能を変更したら、selfIntroductionの関数にエラーが発生します.あなたは今自分がこのような間違いを犯すことはできないと感じるかもしれませんが、ウェブページが複雑になり、多くの人が守る時、これは見つけにくいバグです.
    2.3純関数プログラミングの利点
  • キャッシュ可能性のある純関数は、入力に応じてキャッシュすることができる.キャッシュを実現する典型的な方法は、memoize技術である.
            var memoize = function(f) {
                var cache = {};
    
                return function() {
                    var arg_str = JSON.stringify(arguments);
                    cache[arg_str] = cache[arg_str] ? cache[arg_str] + '(from cache)' : f.apply(f, arguments);
                    return cache[arg_str];
                };
            };
    
            var squareNumber  = memoize(function(x){ return x*x; });
    
            console.log(squareNumber(4));       
            console.log(squareNumber(4)); 
            console.log(squareNumber(5));
            console.log(squareNumber(5)); 
    実行結果:
    16
    16(from cache)
    25
    25(from cache)
  • 移植可能な純関数は完全に自給自足であり、必要なものはすべて簡単に入手できる.この点をよく考えてみると…この自給自足のメリットは何ですか?まず,純粋な関数の依存性が明確であるので,より簡単に観察し,理解することができる.これはこのコードを読む時に、他の関数や変数に依存しない機能を一つの関数で完成させます.
  • は、第3のポイントをテストすることができます.純粋な関数は、テストをより簡単にします.入力するたびに同じ結果が出力されるので、同じ入力を何回もテストする必要はありません.
  • 合理性(Reasonable)多くの人が純粋な関数を使う一番の利点は透明性を引用することだと信じています.このコードが結果を実行し、プログラムの動作全体を変えない前提で代替される場合、このコードは透明性を参照しているということです.純関数はいつも同じ入力から同じ出力を返すことができるので、それらは常に同じ結果を返すことを保証します.これは参照の透明性を保証します.
  • 並列コード(Paralel)の最後の点も決定的な点である.任意の純粋な関数を並列に実行することができる.純粋な関数は共有メモリにアクセスする必要は全くないので、その定義によって純関数も副作用によって競合状態に入ることはない.パラレルコードは、サーバーのjs環境とウェブワーカーのブラウザを使用していますので、スレッドを使用しています.しかし,非純粋関数の複雑さを考慮すると,現在主流の観点では,このような並列の使用は避けられている.
  • 3.関数式プログラミングや純関数を乱用しない
    以上の解析は純関数の多くの利点を述べたが、これは私たちが作成する関数ごとに純関数であることを要求しているわけではない.関数が「純粋」であればあるほど、環境に対する依存度が少なくなり、多くのパラメータを入力することを意味します.
    3.1純化できる場合の例
    var pureHttpCall = memoize(function(url, params){
      return function() { return $.getJSON(url, params); }
    });
    ここの面白いところはhttp要求を本当に送っていないことです.ただ一つの関数を返して、呼び出した時にお願いします.この関数が純粋な関数になる資格があるのは、いつも同じ入力に従って同じ出力を返します.urlとparamsを与えた後、http要求を同じ送信する関数だけを返します.この技术はコリメートとコードの组み合わせによって、私たちのJSコードがはっきりして、メンテナンスできます.
    コリックは私がブログを書いたことがあります.クリックして読むことができます.コードの組み合わせは実は比較的簡単で、以下のように挙げられます.
            var compose = function(f, g) {
                return function(x) {
                    return f(g(x));
                };
            };
    
            function getPeople(){
                return {};
            }
    
            function namePeople(p){
                p.name = 'Lee';
                return p;
            }
    
            var definePeople = compose(namePeople, getPeople);
            var people = definePeople();
            console.log(people);
    実行結果:Object{name:「Lee」}見て、コードの組み合わせは文字通りです.
    3.2関数式プログラミングの濫用例
    var getServerStuff = function(callback){
      return ajaxCall(function(json){
        return callback(json);
      });
    };
    //           ,     
    var getServerStuff = ajaxCall;
    分析プロセス:
    //   
    return ajaxCall(function(json){
      return callback(json);
    });
    
    //      
    return ajaxCall(callback);
    
    //   ,    getServerStuff
    var getServerStuff = function(callback){
      return ajaxCall(callback);
    };
    
    // ...   
    var getServerStuff = ajaxCall;
    このようなコントローラがあります.
    var BlogController = (function() {
      var index = function(posts) {
        return Views.index(posts);
      };
    
      var show = function(post) {
        return Views.show(post);
      };
    
      var create = function(attrs) {
        return Db.create(attrs);
      };
    
      var update = function(post, attrs) {
        return Db.update(post, attrs);
      };
    
      var destroy = function(post) {
        return Db.destroy(post);
      };
    
      return {index: index, show: show, create: create, update: update, destroy: destroy};
    })();
    直接に書き換えることができます.
    var BlogController = {index: Views.index, show: Views.show, create: Db.create, update: Db.update, destroy: Db.destroy};
    関数式のプログラミングを乱用すると、コードが分かりにくいかもしれません.したがって、私の提案は、コードは最も直接的で簡潔な状態を維持し、できるだけピュア関数(メンテナンスしやすい)を使用し、適切な場合は関数式プログラミングを使用する(分かりやすい)ことです.