unhurried

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

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をオフにする

キーマップを設定する

PlantUML Class Diagram

I made a class diagram with PlantUML followed by the previou sequence diagram.

' Write comments after single quotaion mark.

@startuml

' Color Setting
skinparam classBorderColor black
skinparam classArrowColor black
skinparam noteBorderColor black
skinparam classAttributeIconSize 0

' Class Definition
class ClassA {
  - privateMember : Integer
  + publicMember : Integer
  # protectedMember : Integer
  ~ packageMember : Integer
  + method() : void
  + {static} staticMethod() : void
  + {abstract} abstractMethod() : void
}
' Note (For Class)
note top of ClassA: Note
note right of ClassA
  Note
end note

' Interface
interface Interface
ClassA ..|> Interface
' Note(Linkに対して)
note on link: Note

' Abstract Class
abstract AbstractClass
ClassA --|> AbstractClass

' Association
ClassA -- ClassB

' Composition
ClassA --* ClassC

' Aggregation
class ClassD
ClassA --o ClassD

' Dependency
class ClassE
ClassA ..> ClassE

@enduml

Generated Diagram

http://www.plantuml.com/plantuml/png/XLBBIiD05DtdAowuy4N5jGj1MnU2kl4B9fCn3ardmcHKV5H80gBetiVEZLR5AheGeTIF6OhT-GlUIL9R9M0Mayavvzuvzqp6mIIl9hqsAQx79duooPlT9yycVJ09VjwRj6FI8xjQwjQoSRlNFJFnGxzrqVzuDl6bIKuTVCQXV_BIRptssbTELESY98eqW0OaYXQbyfYgoa0gS0D2wty52qh9dN9UICt-hzTQSNTBisKgnJhVOp1h3TaPqZkJlAEdp-wTascXcbN0lWDGWL3nRQBPAckuJC4SB0lDVAQGcuPmomquBQ76i0njKSsy4lOGGhH8_FAc3QOtfJSnYV2sv5w6xKUQQ4wRaBzNooJ4ZRGYL3TXiFihQ-B4QsZhgtESZOXtr7lgclZSn3TVdHF79WbQXY0tYWpcCdrEAEvlwY65XsOS4rvs1JPEEuRQ89GvVB0RmOfOPsOEvaVG7qihNDHB7Ka10NA55onOAERBdJY3SN_m72wEgrJmkD-CRH55adBCKegXCE-tQFcgR8Gouh_vgLnGpHhulc9-tc3ahwaDrNA0eBh6Gio92RexAbuQHZ9V8Dy0

PlantUML クラス図

前回のシーケンス図に続いてクラス図も作成してみました。

' コメントはシングルクオーテーションの後に記述する

@startuml

' 色設定
skinparam classBorderColor black
skinparam classArrowColor black
skinparam noteBorderColor black
skinparam classAttributeIconSize 0

' クラス定義
class ClassA {
  - privateMember : Integer
  + publicMember : Integer
  # protectedMember : Integer
  ~ packageMember : Integer
  + method() : void
  + {static} staticMethod() : void
  + {abstract} abstractMethod() : void
}
' Note(Classに対して)
note top of ClassA: Note
note right of ClassA
  Note
end note

' Interface
interface Interface
ClassA ..|> Interface
' Note(Linkに対して)
note on link: Note

' Abstract Class
abstract AbstractClass
ClassA --|> AbstractClass

' Association
ClassA -- ClassB

' Composition
ClassA --* ClassC

' Aggregation
class ClassD
ClassA --o ClassD

' Dependency
class ClassE
ClassA ..> ClassE

@enduml

生成される図

http://www.plantuml.com/plantuml/png/XLBBIiD05DtdAowuy4N5jGj1MnU2kl4B9fCn3ardmcHKV5H80gBetiVEZLR5AheGeTIF6OhT-GlUIL9R9M0Mayavvzuvzqp6mIIl9hqsAQx79duooPlT9yycVJ09VjwRj6FI8xjQwjQoSRlNFJFnGxzrqVzuDl6bIKuTVCQXV_BIRptssbTELESY98eqW0OaYXQbyfYgoa0gS0D2wty52qh9dN9UICt-hzTQSNTBisKgnJhVOp1h3TaPqZkJlAEdp-wTascXcbN0lWDGWL3nRQBPAckuJC4SB0lDVAQGcuPmomquBQ76i0njKSsy4lOGGhH8_FAc3QOtfJSnYV2sv5w6xKUQQ4wRaBzNooJ4ZRGYL3TXiFihQ-B4QsZhgtESZOXtr7lgclZSn3TVdHF79WbQXY0tYWpcCdrEAEvlwY65XsOS4rvs1JPEEuRQ89GvVB0RmOfOPsOEvaVG7qihNDHB7Ka10NA55onOAERBdJY3SN_m72wEgrJmkD-CRH55adBCKegXCE-tQFcgR8Gouh_vgLnGpHhulc9-tc3ahwaDrNA0eBh6Gio92RexAbuQHZ9V8Dy0

PlantUML Sequence Diagram

I made a sample sequence diagramAtom with PlantUML. Most frequently used rules are in this sample, so you should be able to make your own diagrams by modifying this sample if you forget the rules.

' Writes comments after singl quotaion mark.

@startuml

' Color Setting
skinparam actorBorderColor black
skinparam sequenceParticipantBorderColor black
skinparam sequenceLifeLineBorderColor black
skinparam sequenceArrowColor black
skinparam noteBorderColor black

' Declaire participants.
' You can define an alias usibg "as".
actor Actor as A
participant Participant as P

A -> P : Synchronous Message
activate P
A <<-- P : Return Message
deactivate P

A ->> P : Asynchronous Message
A -> A : Message to Self

' Note (For messages)
A -> P : Message
note right
 Note: note (left|right) ... end note
 (Or just "note (left|right) : ...")
end note
' Note (For participants)
note over A, P : note (left|rigght|over) of

' Text Decoration
A -> P : Multi Line\n(Line Break)
A -[#blue]> P : <font color="blue"><b>Change Style</b></font>

' Conditional Branch
alt guard condition
  A -> P : Message
else guard condition
  A -> P : Message
end

' "Message to Self" doesn't work with "Activate" well.
A ->> P : Asynchronous Message
activate P
P -> P : Message to Self
deactivate P

@enduml

Generated Diagram

http://www.plantuml.com/plantuml/png/ZLDTJnD157tVNt5J7qeJAUzDRRBmwaSZZ-h3T3kbQzSPd9t5aF3Wx2gsLW8nGTJW0o58XT0IJDH4g3zcM5YV-0lEJB4k7udTPAUxzvwUUy-vTxCWmwyosf2HEfjIz6JuNJ-7-pBQbQ5wtP7HeOmMzQbJspehkd7_ZHIxIUTzycjVYWyoR5lgoaBIEaWwUt7te-KtN3AB67e2oE6KJL9MnMoAUfH1nKDE8mNmyTC04mUN4UEkuyuYmaU1ttPhwYPu5ApD67rsFOHGVWs5qf9gHolkRYQTvsU7pTDrSRgw5I_takZexB2bWCYNOZjkRiKhIp9ygyIVzh-eY1HBsXSZ7snp8XziQ_OVRxg6JfOjouRn4fIX0DFpnAapIcZWmnti-sW6Qovt3d6iW3OKY-FZ1daVyu2H8QYAKp33Do2q_MiOJJbRPSy3m2bCOw-c3RYhd562ppDgtd6lByMQ59-rxc6ZVwcqaS3ScJgtp5yBnbeOyt2DBvXu3lBvF61IDHaBnkunU1pu739NaGMDpUIi8NhOpuMvNEWfE-Y1pc5b-4tJt4LYnRoWipcWHk79krVntbgo-VBtPZybAF2u2tgr7f8n_GEJ3AD6JYCUtAXu0Nuq01PhLCtDqMjpAwFZcLAnKfgg8wACdERp7YvEL4h52GqhcNgVDev_VekRY_71YeKy3ZC1ObN5GAekTocn0AxOYZqVZuGZLLtYqXpL9ssVRuCKdUCVhqzQRIcMu_RgyT6wCMv7YXV_svBKGfKlrHqkJ7hj_W00