unhurried

コンピュータ関連ネタがほとんど、ときどき趣味も…

Mission Peak 春と夏の景色

ベイエリアでは11月頃~3月頃が雨季でこの期間はよく雨が降るのですが、それ以外の時期にはほとんど全くと言って良いほど雨が降りません。
サンノゼから少し北に行ったところにMission Peakという人気のハイキングトレイルがあり、この山は雨季に青々と成長した草が、夏には一気に枯れて茶色に変化します。
このわずか2、3ヵ月の間の変化を記録してみるとおもしろいのではと思い、春(4月)と夏(7月)に同じ場所から写真を撮って比較してみました。

f:id:unhurried:20170721153121j:plain:w500

f:id:unhurried:20170721153432j:plain:w500

f:id:unhurried:20170721153458j:plain:w500

参考

React Quick Startを簡単に復習できるサンプルコード

Webアプリのアイデアがふと思い浮かんだので、開発するときにこれまで使ったことのないライブラリを導入してみようと、Reactに挑戦しています。
Reactはドキュメントが充実していて大変素晴らしいのですが、私は物覚えが悪いので一度読んでもすぐに忘れてしまうことが多いです。ですのでいつも後でさっと見返せるように自分用に簡単なメモを作るのですが、このメモがもしかするとどなたかの役に立つかもしれないと思いましたので、ご紹介します。

今回はReact Quick Startをポイントをまとめたサンプルコードです。
(CodePenにも登録しています:React Cheat Sheet

// React Element

// Tag syntax like "<p> ... </p>" is called JSX, and JSX produces a React element.
// React elements are the descriptions of what you want to see on the screen.
// React elements are imuutable and they can't be changed once they are defined.
// (See following React components for how you can update the DOM.)
const singleLineElement = <p>Hello World.</p>;

// When you split JSX over multiple lines, 
// it is recommended to wrap it in parentheses to avoid automatic semicolon insertion by editors.
// Expressions can be embedded in JSX. (JSX itself is also an expression.)
// React escapes any values embedded in JSX before rendering them to prevent injection attacks.
const href = "https://www.time.gov/";
const multipleLinesElement = (
    <p>
        <a href={href} >Current Time: {new Date().toLocaleTimeString()}</a>
    </p>
);

// React Component
// React components accept inputs (props) and return React elements.
// They can be defined with "function" or "class", and used in JSX format like "<MyComponents />".

// React component in the "class" style
// React component extends React.Component class.
// Component name (= class name) must be started with a capital letter.
class ClassComponent extends React.Component {

    // Constructor recieves JSX attributes as a single props object.
    // React component must not modify its own props.  
    constructor(props) {

        // Constructor should always call the base constructor with props.
        super(props);

        // this.state is a special object used to store the state of the component.
        // this.state can be initialized only in the constructor.
        this.state = {value: 1};

        // Assign "this" to methods to handle events.
        this.handleChange = this.handleChange.bind(this);
    }

    // Define a method to handle an event.
    // React passes SyntheticEvent according to the W3C spec.
    handleChange(event) {

    // Use setState() method to modify the state.
    // setState() merges the object you provide into the current state.
    this.setState({value: event.target.value});
    // Use callback when you modify the state based on previous state.
    // (The update of the state may be asynchronous.)
    //     this.setState((prevState, props) => ({
    //         counter: prevState.counter + props.increment
    //     }));
    }
    
    // render() returns a single root React element which will be shown in the DOM. 
    // If render() returns null, the component will not be rendered.
    render() {
        return (
            <div>
                <form>
                    <input type="number" value={this.state.value} onChange={this.handleChange} />
                </form>
                <FunctionComponent number={this.state.value} />
            </div>
        );
    }

    // componentDidMount() is called after the component output has been rendered to the DOM.
    componentDidMount() {
        console.log("componentDidMount()");
    }

    // componentWillUnmount() is called when the component is being removed from the DOM.
    componentWillUnmount() {
        console.log("componentWillMount()");
    }
}

// React component in the "function" style
function FunctionComponent(props) {

    const number = props.number;
  console.log(number);
    var listItems = [];
    // When you define a list, a "key" attribute needs to be specifined in every element of the list.
    // (Keys help React identify which items have changed, are added, or are removed.)
    // Keys used within arrays should be unique among their siblings.
    for(var i=0; i<number; i++) {
// for(var i=0; i<number; i++) {
        listItems.push(
            <li key={i.toString()}>{i+1}</li>
        );
    }

    return (
        <ul>{listItems}</ul>
    );
}

// ReactDOM.render() renders React components (or elements).
// Pass a component or an elemment to render and root DOM where you want to show it.
// React DOM updates only necessary parts of the DOM.
ReactDOM.render(
    <ClassComponent />,
    document.getElementById('root')
);

JavaScript エラー処理(独自エラー・条件分岐)

Javaで例外処理と言えば、Exceptionクラスを継承したクラスを定義して、instanceof演算子で条件分岐するのが定番です。JavaScriptの定番について調べてみたところ、大きくは下記の2パターンがあるようです。

  1. Errorオブジェクトを継承する

    • Errorオブジェクトを継承したクラス(ES5以前の場合はprototypeを継承したオブジェクト)を定義して、instanceof演算子で条件分岐します。
    • instanceof演算子で比較するため、文字列比較と異なり確実に比較ができますが、条件分岐をするコードでも独自Errorクラス(オブジェクト)をロードする必要があります。
  2. Errorオブジェクトのnameプロパティを変更する

    • Errorオブジェクトを生成して、nameプロパティを変更して、nameプロパティに設定した文字列一致で条件分岐します。
    • 手軽に記述できる一方、文字列一致のため予期せず依存するモジュールに定義されているエラーとnameプロパティが重なってしまう可能性があります。

サンプルコード

error.js

var ERROR = {};

// Errorを継承したままではnameプロパティは「Error」のままであるため、
// nameプロパティを独自のエラークラスの名称に変更する。
ERROR.Error1 = class extends Error {}
ERROR.Error1.prototype.name = "Error1";

// ES5以前の場合はprototypeを使って継承する。
ERROR.Error2 = function (message) {
    this.message = message;
}
ERROR.Error2.prototype = new Error;
ERROR.Error2.prototype.name = "Error2";

module.exports = ERROR;

module.js

const ERROR = require('./error.js');

class MyClass {
    static myMethod(arg) {
        if (arg === 1) {
            throw new ERROR.Error1("Error1 occured.")
        } else if (arg === 2) { 
            throw new ERROR.Error2("Error2 occured.")
        } else if (arg === 3) { 
            const e = new Error("Error3 occured.")
            e.name = "Error3";
            throw e;
        }
    }
}

module.exports = MyClass;

main.js

const MyClass = require('./module.js');
const ERROR = require('./error.js');

try {
    MyClass.myMethod(1);
} catch(e) {
    if (e instanceof ERROR.Error1) {
        console.log(e.name);
        console.log(e.message);
    }
}

try {
    MyClass.myMethod(2);
} catch(e) {
    if (e instanceof ERROR.Error2) {
        console.log(e.name);
        console.log(e.message);
    }
}

try {
    MyClass.myMethod(3);
} catch(e) {
    if (e.name === "Error3") {
        console.log(e.name);
        console.log(e.message);
    }
}

実行結果

$ node main.js
Error1
Error1 occured.
Error2
Error2 occured.
Error3
Error3 occured.

参考

JavaScript オブジェクト指向

私はJavaを長く使ってきたためオブジェクト指向の考え方は馴染み深いのですが、JavaScriptではを使うときにはあまり意識していませんでした。JavaScriptはクラスを定義する方法がいくつかあってややこしいので、簡単に整理してみました。

  • ES5以前ではclass構文が利用できないためfunctionを利用します。
var ES5Class = (function() {

    // コンストラクタ
    var ES5Class = function(member) {
        // new演算子を指定せずに下記のように呼び出されたときための対策
        // var instance = ES5Class();
        if(!(this instanceof ES5Class)) {
            return new ES5Class(member);
        }

        // メンバ変数
        // プライベートにしたい変数には慣習的にアンダースコアを付ける。
        // (実際には外部からのアクセスは可能であるが、変数の用途を明示するため。)
        this._member = member;
    };

    // プロトタイプを使ってメソッドを定義する。
    // this.printMethod = function... でも定義できるが、
    // インスタンス生成の度に関数が作成されるため効率が悪い。
    var p = ES5Class.prototype;
    
    // メソッド
    p.printMember = function () {
        console.log(this._member);
    }

    return ES5Class;
})();

module.exports = ES5Class;
  • ES6以降ではclass構文を使ってクラスの定義ができます。
class ES6Class {

    // コンストラクタ
    constructor(member) {
        // メンバ変数
        // プライベートにしたい変数には慣習的にアンダースコアを付ける。
        // (実際には外部からのアクセスは可能であるが、変数の用途を明示するため。)
        this._member = member;
    }

    printMember() {
        console.log(this._member);
    }
}

module.exports = ES6Class
  • class構文では静的メソッドも定義できますが、静的メンバは定義できません。
// class構文には静的メンバを定義する方法は用意されていないため、
// 代替手段として、グローバルスコープの変数を利用する。
var _member = 0;

class ES6StaticClass {

    // 静的メソッド
    static printMember() {
        _member++;
        console.log(_member);
    }
}

module.exports = ES6StaticClass

Node.js モジュール読み込み

Node.jsのモジュール読み込みではmodule.exports、exportsが利用できますが、違いがいまいち理解できていなかったので自分なりに整理してみました。
実装上の違いといった細かいことはリンク先にとても詳しく記載されていますので、今回は使い分け方に焦点を当てています。

module.exportsとexportsの使い分け

単一のクラスや関数、オブジェクトをエクスポートする場合はmodule.exportsを使います。

class MyClass {
    ...
}
module.exports = MyClass;

複数の関数やオブジェクトを持つオブジェクトをエクスポートする場合はexportsを使います。

function myFunc1 {
    ...
}
function myFunc2 {
    ...
}
exports.myFunc1 = myFunc1;
exports.myFunc2 = myFunc2;

※ module.exportsを利用しても記述できますが、exportsの方がシンプルに記述できます。

module.exports = {
    myFunc1: myFunc1,
    myFunc2: myFunc1
}

requireを複数回実行したときの挙動

初回ロード時のみ評価され2回目以降はキャッシュされたオブジェクトが返却されます。

  • module.js
var _var = Math.random();
function func() {
    console.log(`module.js - func : _var=${_var}`)
}
exports.func = func;
  • main.js
const module1 = require('./module.js')
const module2 = require('./module.js')
module1.func()
module2.func()
  • 実行結果
module.js - func : _var=0.7381836391077983
module.js - func : _var=0.7381836391077983

参考

vim-mode-plusのキーバインドを変更する

Atomエディタプラグインvim-mode-plusには通常のVimエディタにあるvimrcを読み込む機能がないため、Atomのキーマップ設定ファイルであるkeymap.csonに設定し、複数のコマンドを割り当てたい場合はAtomの設定ファイルであるinit.coffeeに複数コマンドを1つのコマンドとして登録する必要があります。

例として、以下のvimrcファイルをinit.coffeeとkeymap.csonに変換する例をご紹介します。

変換前

vimrc

noremap j gj
noremap k gk

noremap <S-j> 10j
noremap <S-k> 10k
noremap <S-h> 10h
noremap <S-l> 10l

変換後

init.coffee

atom.commands.add 'atom-text-editor.vim-mode-plus', 'custom:move-left-10', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'vim-mode-plus:set-count-1'
  atom.commands.dispatch view, 'vim-mode-plus:set-count-0'
  atom.commands.dispatch view, 'vim-mode-plus:move-left'

atom.commands.add 'atom-text-editor.vim-mode-plus', 'custom:move-down-screen-10', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'vim-mode-plus:set-count-1'
  atom.commands.dispatch view, 'vim-mode-plus:set-count-0'
  atom.commands.dispatch view, 'vim-mode-plus:move-down-screen'

atom.commands.add 'atom-text-editor.vim-mode-plus', 'custom:move-up-screen-10', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'vim-mode-plus:set-count-1'
  atom.commands.dispatch view, 'vim-mode-plus:set-count-0'
  atom.commands.dispatch view, 'vim-mode-plus:move-up-screen'

atom.commands.add 'atom-text-editor.vim-mode-plus', 'custom:move-right-10', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'vim-mode-plus:set-count-1'
  atom.commands.dispatch view, 'vim-mode-plus:set-count-0'
  atom.commands.dispatch view, 'vim-mode-plus:move-right'

keymap.cson

'atom-text-editor.vim-mode-plus:not(.insert-mode)':
  'shift-h': 'custom:move-left-10'
  'shift-j': 'custom:move-down-screen-10'
  'shift-k': 'custom:move-up-screen-10'
  'shift-l': 'custom:move-right-10'
  'h': 'vim-mode-plus:move-left'
  'j': 'vim-mode-plus:move-down-screen'
  'k': 'vim-mode-plus:move-up-screen'
  'l': 'vim-mode-plus:move-right'

AtomにVimキーバインドを設定する

学生の頃からVimを愛用して来ましたが、用途によってはプラグインが豊富なAtomの方が便利な場合があるので、AtomVimキーバインドを設定する方法を調べてみました。

プラグインのインストー

Escで挿入モードを抜けたときにIMEをオフにする

キーマップを設定する