著者アーカイブ 杉浦

著者:杉浦

javascriptの静的解析ツールESLint

ESLint – Pluggable JavaScript linter
 ESLintはjavascriptのためのlintです。lintはプログラムが動作不能になるような致命的な文法エラーのみならず様々なものを発見する目的で使用されます。発見したいものは例えば、間違いを犯しやすい記述であるアンチパターン、インデントの乱れのようなコードスタイルのぶれ、です。ESLintの特長は次の様な拡張性にあります。

  • すべての検証ルールを自由にon/offできる
  • 260個以上(5.5.0時点)の豊富なルールがある

  •  この特長のため自分のプロジェクトに合わせたカスタムルールを簡単に作ることができます。また、ECMAScript 2015 (ES6), 2016, 2017, 2018, 2019を標準サポートしており、新しくなった記法を使う様に自身を矯正することもできます。
     ESLintはnpmで導入できます。
    eslint – npm
     installはプロジェクトのルートをcdにして次のコマンドを実行です。

    npm install --save-dev eslint

     同じくプロジェクトのルートに.eslintrc.jsonを置いて、以下の記述を書き込んで簡単な初期設定が完了です。

    {
        "extends": ["eslint:recommended"],
        "parserOptions": {},
        "env": {"browser": true},
        "globals": {},
        "rules": {}
    }

     実行は

    eslint src/js

    のみです。これでsrc/js以下がeslintにかけられます。
     
     上述した通り、eslintには多数のルールがあります。その一つ一つを管理するのは手間です。そのためまずはextendsでルールセットを継承します。ここではeslintのおすすめにしてありますが、プラグインとして様々な設定が公開されています。googleなんかも公開しています。
     parserOptions,env,globalsには実行環境の構文なり、ライブラリなりを書き込みます。これを設定しないと問題が無いにも関わらず、グローバル変数、関数を宣言していない、その構文は成り立たない、と怒られます。

    //設定例
        "parserOptions": {
            "ecmaVersion": 2018
        },
        "env": {
            "browser": true,
            "es6": true,
            "jquery": true
        },
        "globals": {
            "L": false,
            "google": false,
            "cloneLayer":false
        },
    

     eslint src/jsを実行した際にsrc/js/lib以下にライブラリが入っているとなるとeslintがライブラリにも怒り始めます。.eslintignoreという設定ファイルでこれを制御できます。書き方は色々なignoreファイル同様、対象に含みたくないファイルのパスを羅列するだけです。

    **/lib/*

     eslintにかけた対象が既存のコードであったり、一気に組み立てた初期のコードであったりする場合、大量のerrorとwarningが出力されます。そしてその大半はいちいち手作業で直すのが嫌になる様な手間な部分です。–fixオプションを付けることでインデント、クォート、セミコロンの様な単純なミスをeslint側で修正してくれます。

    eslint src/js --fix
    著者:杉浦

    正規表現で行に文字列を含むことのNOT,AND,OR

     正規表現のlookaheadは文字列を消費しません。^(?=.*hoge)で行全体を先頭からlookaheadできます。この二つを用いることで、ある文字列を行に含む、含まないの組み合わせを正規表現で自由に作ることが出来ます。基本パーツは次の四つです。

    ^(?=.*hoge).*$ hogeを含む行
    ^(?!.*hoge).*$ hogeを含まない行 not A
    ^(?=.*hoge)(?=.*fuga).*$ hogeとfugaを含む行 A and B
    ^((?=.*hoge)|(?=.*fuga)).*$ hogeかfugaを含む行 A or B

     hoge,fugaにはそれぞれのかっことその中身を当てはめられます。これらを組み合わせて、好きに長い検索ができます。ABとBCの組み合わせかCDを含み、DEかCAを含まない行ならば((AB and BC) or CD) and not (DE and CA)であり、
    ^((?=.*AB)(?=.*BC)|(?=.*CD))(?!.*(?=.*DE)(?=.*CA)).*$

    となります。

    著者:杉浦

    webページ評価ツールLighthouse

     Lighthouse – Chrome ウェブストア
     Lighthouse によるウェブアプリの監査 | Tools for Web Developers
    | Google Developers

     Lighthouseはwebページの評価を行ってくれるツールです。お手軽な使い方は上記リンクのウェブストアからChrome拡張機能としてのLighthouseを導入し使用することです。オプションもコマンドも必要なく、ただ起動するだけで監査を行い、結果を出力します。 Lighthouseの評価は下図の様にPerformance,ProgressiveWebApp,Accessibility,Best Practices,SEOに分かれています。

     それぞれ、Performanceはおおよそ速度、データ量関連に問題が無いか、ProgressiveWebAppはネイティブアプリのようにふるまえるか、Accessibilityはわかりやすさ、Best Practicesは良いとされるやり方をしているか、SEOは検索エンジン最適化がされているか、です。画像の更に下の方には細分化された指摘がいくつもあります。
     Lighthauseの特徴は、googleが奨励しているPWAとしての評価がgoogle自身の規定でできる、という点です。またSEOは大概眉唾物ですが検索エンジン大手のgoogleが提供するツールが評価するSEOですから多少は当てになるでしょう。
     これが高得点ならばよいwebページというわけはないです。PWAは特に顕著でアプリである必要のないページは多々あります。基本的にぱっと見の第一印象がどうか、という点から評価がなされます。ただ高得点のページならば低得点のページよりも快適に使用しやすいのではないでしょうか。
     また、Lighthouseの指摘により今まで知らなかったより良いやり方を知ることもできます。

    著者:杉浦

    google mapのタイムライン機能

     googleはユーザからこれでもかと情報を集めて、その情報をサービスに活かしています。タイムライン機能は自分がいつどこにいたかの情報を見る機能です。
     使い方はgoogle map左上の三みたいなアイコンをクリックして図のタイムラインをクリック。

     主要な点、おそらく遠出した時の点が描かれる画面になります。左上から日付を指定すると下の画像の様に、具体的なルートとかかった時間まで教えてくれます。

     残したくない、消したいという場合は右下のごみ箱から捨てられます。一方でローカルに保存することもできます。このようなデータを使用しているため、googleのナビは同じルートでも個人差を考慮したより正確であろう移動時間を予測してくれます。
     

    著者:杉浦

    letとvar

     let – JavaScript | MDN
     letはECMAScript2015で標準化された記法のひとつです。上のリンク先には下の様に記述されています。

    let 文はブロックスコープの局所変数を宣言します。任意で値を代入して初期化できます

     具体的にどういうことかというと下図の様な感じです。varはブロックスコープ内部でvarを付けて再宣言しても別の変数として扱ってくれません。一方でletは再宣言をすれば別の変数と扱ってくれます。

     もちろん、宣言なしなら同じ変数として扱ってくれます。

     特に役に立つのは次の様な実行タイミング毎の処理です。
    参考:varよりすごいletとconst。(現代的JavaScriptおれおれアドベントカレンダー2017 – 02日目) | Ginpen.com
     

     javascriptのsetTimeout()は、コールバックの実行を待機して、タイミングが合ったらコールバックを実行、それ以外の部分は待機関係なしに続行、という処理を行います。setTimeoutの待機後のコールバックで使用される変数の値が待機中に変更されたら、待機を始めた時の値ではなく、その変更された値を参照して実行を行います。varの場合、スコープがループ全体にかかっているため、変数iはすべて同じ変数です。そのため、ループ終了後の値である5のみが表示されました。一方でletはループ毎に異なる変数として扱われます。そのため0,1,2,3,4と順々に表示されました。i++は次のループのlet iに渡す値の決定に関わるのみで、今のループのiを変更していない感じ

    著者:杉浦

    関数のまとめ係の関数

     構造化プログラミングには抽象という手法があります。この抽象を利用して自然言語から順にコードを書き下していくことができます。とりあえず書いてみようの精神による混乱が少なくなります。

    抽象(abstraction):プログラムのブロックなどに名前をつけ、さらに中身を見ないで正しいと仮定することで検証作業を後回しにする操作

     例えば選択されたファイルに応じてマーカーを選択されたファイルのリストと地図上の二つに追加する関数を考えます。

    
    function add_selected_marker(filepath) {
    //新しいマーカーのアイコンの決定
    //マーカーのアイコンを選択されたファイルのリスト(selectbox)に追加して、更新をかける
    //選択されたファイルの情報とマーカーのアイコンを地図の上に載せる
    //地図の上に載せ終わっていたら、選ばれたファイルのリスト中のアイコンをクリックしたときのイベントを追加する
    //地図の上に載せ終わっていたら、追加したマーカーの位置に画面中央を移動させる
    }
    

     リストに追加する際のアイコン以外の処理はここ以外で既にやってあります。このコメントを関数に置き換えます。

    
    function add_selected_marker(filepath) {
        const icon_index = marker_managements.get_new_icon_index();
        update_icon_img_in_selectbox(icon_index);
    
        add_marker_on_map(filepath, icon_index).then(() => {
            add_event_click_marker_in_list(filepath, icon_index);
            set_map_center_to_marker_point(filepath);
        });
    }
    

     この関数自体では具体的なコードはさっぱりですが、抽象化された動作から何をする関数なのか大体わかります。抽象を利用するとこのような他で記述した関数をまとめる関数が作れます。

    著者:杉浦

    javascriptのコンパイラであるbabelの紹介

    Babel · The compiler for next generation JavaScript
     javascriptはES6という便利な記法があります。これから先も少しずつ記法が増え、javascriptプログラムをより楽に正確に書くための言語も増えていくでしょう。しかしながら、ブラウザにこれら全てを生のまま対応しろというのは酷な話です。javascriptのみでも未対応の関数、記法は少なからずあります。babelは近年の多様な言語を多くのブラウザで読めるようにコンパイルしてくれるプログラムです。
     具体的に何ができるかというと

    a => b
    let d = `d`;
    const e = `e`;
    `abc${d}
    ${e}
    `
    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        distance() {
            return Math.sqrt(this.x * this.x + this.y * this.y);
        }
    }
    new Point(1, 2)
    

    こんなのが

    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    (function (a) {
        return b;
    });
    var d = "d";
    var e = "e";
    "abc" + d + "\n" + e + "\n";
    
    var Point = function () {
        function Point(x, y) {
            _classCallCheck(this, Point);
    
            this.x = x;
            this.y = y;
        }
    
        _createClass(Point, [{
            key: "distance",
            value: function distance() {
                return Math.sqrt(this.x * this.x + this.y * this.y);
            }
        }]);
    
        return Point;
    }();
    new Point(1, 2);
    

     こんなのになります。後者の文量は前者の5倍以上です。classは元々のjavascriptになかっただけあってとても巨大になっています。この記述の変換によって、便利な記法で書いたjavascript用プログラムがIEでも難なく動きます。Try it outのでブラウザ上のままお試しが出来ます。
     babelはnpmで導入できます。installはプロジェクトのルートをcdにして次のコマンドを実行。

    npm init
    npm install --save-dev @babel/core
    npm install --save-dev @babel/preset-env
    npm install --save-dev babel-cli
    npm install --save-dev babel-core
    npm install --save-dev babel-preset-minify
    

     同じくプロジェクトのルートに.babelrcを置いて、以下の記述を書き込んで簡単な初期設定が完了。

    {
    "presets": [
      ["env",{
        "targets": {
          "browsers": ["last 2 versions", "ie 11"]
        },
        "modules": false,
        "useBuiltIns": true
      }],
      ["minify",{
        "keepFnName": false,
        "keepClassName": false
      }]
    ]}
    

     minifyの設定があるように圧縮もできます。他にも設定可能な項目は多々あります。
    使いかたは次の通り、次のコマンドでsrc以下がdist以下にコンパイルされます。

    babel src -d dist

     一度準備さえすればそこからは楽の積み重ねができます。

    著者:杉浦

    空中写真とオルソ画像

     地図は全ての地点において真上から見たような視点で地形が描かれます。しかし空中からの写真撮影はそうはいきません。撮影を行った一点から放射上に広がり、遠くのものほど傾いて映ります。この傾いた写真を空中写真、地図の様に傾きを直した写真をオルソ画像と呼びます。下の図はオルソ画像について|国土地理院から引用した図です。
     
     図の様に写真の端を中央に寄せて真上から見たときと同じ大きさに収めるわけです。ただの圧縮なので傾きによって映った側面の部分は画像に残ったままです。この手法によって写真が整形されているからこそ、下図の様な地図を重ね合わせを少ない違和感で実現できています。

    著者:杉浦

    文字化け解析に役立つサービスもじばけらったの紹介

     文字化け解読ツール「もじばけらった」は文字化けがどの様な変換で起きているかを試すツールを利用できるwebページです。

     使い方は上画像のまま。入力してばけらったすると下画像の様にタブと文字列が出ます。
      
     内タブの文字コードで書かれた文字列が外タブの文字コードでエンコードされると入力された文字列になると示してくれます。いろいろな文字コードの組み合わせで逆変換を試してくれるわけですね。下画像ならSJISっぽい文字コードで書かれたdummy0日本�がUTF-8として見られた結果、dummy0譌・譛ャ隱が出力されたと予想できるわけです。
     文字化けで情報が落ちたら復元できませんし、多重変換、暗黙的な変換なりなんなりが原因となることもあります。単純に解決することは稀ですが、大体大きなヒントになってくれます。
     このサービスは基本的に入力された文字列をphpのmb_convert_encoding($入力文字列,$外タブの文字コード,$内タブの文字コード);で変換してvar_dump()して出力しています。文字化けは誤った変換によるものでその変換は可逆的です。そのため文字化け問題は問題の再現がそのまま解決につながりやすいです。文字化け問題の解決に詰まった時は、もじばけらったの手法を参考にして問題を再現するのも一つの手です。

    著者:杉浦

    構造化プログラミングのさわり

     構造化プログラミングはエドガー・ダイクストラらによって唱えられたプログラミング手法です。デザインパターン、オブジェクト指向のような近年も使われるプログラミング手法のひとつかふたつ前ぐらいの少し昔の手法です。少し昔といえど構造化プログラミングの発展として近年のプログラミング手法があります。構造化プログラミングは基礎的でわかりやすいです。
     構造化プログラミングは大きなプログラムを書いた時点でプログラムの正しさを証明することを目的としています。ここでいう大きな、とは達成するべき課題や目的が複雑であるということです。複雑すぎるプログラムの挙動を把握することは困難です。
     構造化プログラミングにおいて、プログラムは部品の集合と例えられます。今のプログラミング言語における関数なりクラスなりがこの部品の扱いです。構造化プログラミングでは人間が正しいと確信できる範囲の部品を積み上げていくことによって正しいプログラムを作成します。下はwikipediaから引用した箇条書きです。ここから部品のことをブロックと呼称します。
     

    ダイクストラの三つの知性の道具
    1. 数え上げ推論(enumeration):一人の人間の能力でできる範囲でプログラムの命令の妥当性を一つ一つ確認していく作業
    2. 数学的帰納法(mathematical induction):while文など計算機特有の多数の繰り返し文についてのみ数学的帰納法を用いて確認する作業
    3. 抽象(abstraction):プログラムのブロックなどに名前をつけ、さらに中身を見ないで正しいと仮定することで検証作業を後回しにする操作

     数え上げ推論と数学的帰納法によって正しいとされたプログラムのブロックに名前を付け抽象化する。抽象化されたブロックを用いてまたプログラミングを行い、できあがったプログラムのブロックを数え上げ推論と数学的帰納法によって正しいと証明して、その正しいとされたブロックを抽象化して、…と繰り返して最後に大きなプログラムを完成させます。
     この手法は一つ一つのプログラムのブロックが正しいとわかるぐらい、理解しやすくなければ実現できません。この辺りが今も読みやすいコードの書き方が大事にされる理由の一つではないでしょうか。