C# で 実行中 アセンブリ の ファイルパス を 取得する 方法

実行中ファイル または 実行中アセンブリ のファイルパスを取得する サンプルコード を以下にまとめます。

以下の サンプルコード では、実行中アセンブリが存在するディレクトリを取得したいます。 実行中アセンブリのファイルパスを取得した後、 Path.GetDirectoryName() を利用してディレクトリパスを取得しています。

サンプルコード

namespace Sample
{
    using System.IO;
    using System.Reflection;

    public class Config
    {
        /// <summary>
        /// 設定ファイル名
        /// </summary>
        private static string fileName = @"app.config";

        /// <summary>
        /// 設定ファイルパス
        /// </summary>
        private static string filePath = Path.Combine(
            Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
            Config.fileName);

        /// <summary>
        /// Config クラスのインスタンスを初期化します。
        /// </summary>
        public Config()
        {
        }
    }
}

…で、結論ですが、実行中アセンブリ情報が取得できれば良いので、以下のどちらかでファイルパスを取得することができます。

staticなフィールドで取得する場合

Assembly.GetExecutingAssembly().Location

インスタンス中で取得する場合

this.GetType().Assembly.Location

C# で ウィンドウハンドル を 取得する 方法

現在実行中プロセス の ウィンドウハンドル を 取得する 方法 の サンプルコード。

VSTO (Visual Studio Tools for Office) で リボン を作成して、子ウィンドウを Form で作成したはいいが…親ウィンドウの後ろに隠れてしまう問題が発生。 Form で Show するときに、親ウィンドウのハンドルを子ウィンドウに渡せば後ろへ行かなくなるようだったので、 ここではそのサンプルコードを記載します。

namespace AddIn
{
    using AddIn.Dialog;
    using Microsoft.Office.Tools.Ribbon;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    public partial class Ribbon
    {
        private void Ribbon_Load(object sender, RibbonUIEventArgs e)
        {

        }

        private void SearchSplitButton_Click(object sender, RibbonControlEventArgs e)
        {
            this.OpenSearchReplaceWindow();
        }

        private void OpenSearchReplaceWindow()
        {
            // Windowsフォームのインスタンスを作成
            var dialog = new SearchReplaceWindow();

            // Excelのウィンドウハンドルを取得
            var handle = Process.GetCurrentProcess().MainWindowHandle;
            var owner = Control.FromHandle(handle);

            // オーナーを指定してダイアログの表示
            dialog.Show(owner);
        }
    }
}

ウィンドウハンドルの取得自体は以下のコードでできます。

var handle = Process.GetCurrentProcess().MainWindowHandle;

このままでは Form.Show の引数に渡せないので、以下のコードで変換します。

var owner = Control.FromHandle(handle);

たった 2行 のコードですが…これの方法を見つけるのに少し時間を使ってしまいました。。 この記事がだれかの役に立てばと思います!

Node.js + Express における log4js の 使い方

タイトルには「log4js」と記載していますが、実際は「log4js-node」の使い方です。 npmlog4js として登録されているのは、 GitHub の log4js-node なので、ここでも 「log4js-node」 を 「log4js」 として取り上げます。 インストール から 大まかな使い方 までが理解できる程度にまとめてみました。


目次


インストール方法

まずは プロジェクト へ log4js をインストール する方法です。 npm が利用できる コンソール を起動して、プロジェクトルートディレクトリへ移動し、以下のコマンドを実行します。

npm install log4js --save

基本的 な 使い方

基本と(思う)使い方の例を以下にサンプルコードで掲載します。

app.js

// log4js の読み込み
var log4js = require('log4js');

// ログ出力設定
log4js.configure({
  "appenders": [
    {
      "type": "file",
      "filename": "log/system.log",
      "maxLogSize": 20480,
      "backups": 3,
      "category": "system"
    },
    {
        "type": "console"
    }
  ],
  "levels": {
      "system": "ALL"
  }
});

// ロガーの生成
var logger = log4js.getLogger('system');

// ログ出力
logger.fatal('some fatal error message.');  // [2016-07-24 23:35:46.588] [FATAL] system - some fatal error message.
logger.error('some error message.');        // [2016-07-24 23:35:46.592] [ERROR] system - some error message.
logger.warn('some warning mesage.');        // [2016-07-24 23:35:46.592] [WARN] system - some warning mesage.
logger.info('some information message.');   // [2016-07-24 23:35:46.593] [INFO] system - some information message.
logger.debug('some debug message.');        // [2016-07-24 23:35:46.593] [DEBUG] system - some message.

log4js を利用する場合、まずは require() で モジュール を読み込みます。 読み込んだ後は logjs.configure() で 設定 を行います。 設定に従って logger インスタンス を生成し、このインスタンスメソッドを利用することでログ出力を行います。 基本的な流れは log4j や log4net と同じです。

設定ファイル の 使い方

log4js.configure(object) に渡している オブジェクト を 任意の jsonファイル ( ここでは log4js.config.json ) に吐き出して 引数 を オブジェクト からフ ァイルパス へ変更するだけです。

ファイルパスを指定する際には、読み込み元ファイル( 以下のサンプルで app.js )から 設定ファイル( 以下のサンプルで log4js.config.json ) への 相対パス または 絶対パス で指定する点に注意してください。

log4js.config.json

{
  "appenders": [
    {
      "type": "file",
      "filename": "log/system.log",
      "maxLogSize": 20480,
      "backups": 3,
      "category": "system"
    },
    {
        "type": "console"
    }
  ],
  "levels": {
      "system": "ALL"
  }
}

app.js

// log4js の読み込み
var log4js = require('log4js');

// ログ出力設定
log4js.configure('log4js.config.json');

// ロガーの生成
var logger = log4js.getLogger('system');

// ログ出力
logger.fatal('some fatal error message.');  // [2016-07-24 23:35:46.588] [FATAL] system - some fatal error message.
logger.error('some error message.');        // [2016-07-24 23:35:46.592] [ERROR] system - some error message.
logger.warn('some warning mesage.');        // [2016-07-24 23:35:46.592] [WARN] system - some warning mesage.
logger.info('some information message.');   // [2016-07-24 23:35:46.593] [INFO] system - some information message.
logger.debug('some debug message.');        // [2016-07-24 23:35:46.593] [DEBUG] system - some message.

Node.js + Express 環境 での 使い方

実際の利用を想定すると Node.js + Express 環境での利用になるかと思います。 Express の 標準ログ出力 を log4js に置き換える サンプルコード を以下に掲載します。

var express = require('express');
var log4js = require('log4js');

// Express の Application インスタンスを生成
var app = express();

// log4js を準備
log4js.configure('./log4js.config.json');
var logger = log4js.getLogger('system');

// Express の 標準ログ出力を log4js に書き換え
app.use(log4js.connectLogger(logger, {level: 'auto'}));

// テンプレートエンジンを EJS に設定
app.set('views', './views');
app.set('view engine', 'ejs');

// 静的コンテンツの公開
app.use('/public', express.static('public'));

// GET: / の処理
app.use('/', require('./routes/index.js'));

// サーバー起動
app.listen(3000);

// 起動ログ
logger.info('Server running at http://localhost:3000');

Express の 標準ログ出力 を 書き換える場合、 L12 のように app.use() の引数に log4js.connectLogger() の戻り値を与えます。

なお、log4js.connectLogger() の 第2引数 に ログレベル auto を設定すると、以下のような出力になります。

条件 ログレベル
HTTPレスポンス が 3xx の場合 WARN
HTTPレスポンス が 4xx または 5xx の場合 ERROR
その他 INFO

Appender の 種類

Wikiを見る限り、以下の Appender が利用可能なようです。

  • Console
  • File
  • DateFile
  • DateFileSync
  • SMTP
  • Mailgun
  • hook.io
  • GELF
  • Multiprocess
  • Loggly
  • Clustered

ここでは特に使いそうな ConsoleFileDateFile の3種類を取り上げます。

Console

コンソールにログ出力を行うアペンダーです。

{
  "appenders": [
    {
      "type": "console",
      "layout": {
        "type": "pattern",
        "pattern": "[%r] [%[%5.5p%]] - %m%n"
      },
      "category": "console"
    }
  ]
}
type
console を指定します。
layout
ログ出力時のレイアウトを指定します。
category
カテゴリ名を指定します。

File

ファイルサイズでローリングを行うアペンダーです。

{
  "appenders": [
    {
      "type": "file",
      "filename": "cafeaulait.log",
      "maxLogSize": 1024,
      "backups": 3,
      "layout": {
        "type": "pattern",
        "pattern": "[%r] [%[%5.5p%]] - %m%n"
      },
      "category": "cafeaulait"
    }
  ]
}
type
file を指定します。
filename
出力するロのパスおよびグファイル名を指定します。
maxLogSize
ログファイルの最大サイズ(bytes)を指定します。
backups
ログファイルを保持する数を指定します。デフォルト 5。
layout
ログ出力時のレイアウトを指定します。
category
カテゴリ名を指定します。

DateFile

日付や時間で出力ファイルをローリングするアペンダーです。

{
  "appenders": [
    {
      "type": "dateFile",
      "filename": "cafelatte.log",
      "pattern": "-yyyy-MM-dd",
      "alwaysIncludePattern": false,
      "layout": {
        "type": "pattern",
        "pattern": "[%r] [%[%5.5p%]] - %m%n"
      },
      "category": "cafelatte"
    }
  ]
}
type
dateFile を指定します。
filename
基本ファイル名を指定します。
pattern
ファイル名の末尾に付与するパターンを指定します。
alwaysIncludePattern
日跨ぎ時に強制的にログローテートするかどうかを指定します。
layout
ログ出力時のレイアウトを指定します。
category
カテゴリ名を指定します。

Layout の 設定方法

各 Appender は layout を設定することで、出力フォーマットを制御することができます。

{
  "appenders": [
    {
      "type": "console",
      "layout": {
        "type": "pattern",
        "pattern": "%[%r (%x{pid}) %p %c -%] %m%n",
        "tokens": {
          "pid": function() { return process.pid; }
        }
      }
    }
  ]
}
type
レイアウトの種類を指定します。
pattern
type: "pattern" の場合、ログ出力するパターンを定義します。
tokens
pattern 中で定義された「動的トークン」の実態を定義します。

type

type に指定できる値は以下の通りです。

  • messagePassThrough
  • basic
  • colored
  • pattern

おそらく普通に使うのは pattern になるかと思います。 type: "pattern" を指定すると、出力フォーマットを任意に変更することができます。

pattern 以外の出力例は以下の通りです。

type サンプル
messagePassThrough
some fatal error message.
some error message.
some warning mesage.
some information message.
some debug message.
basic
[2016-07-28 23:24:27.941] [FATAL] system - some fatal error message.
[2016-07-28 23:24:27.944] [ERROR] system - some error message.
[2016-07-28 23:24:27.946] [WARN] system - some warning mesage.
[2016-07-28 23:24:27.947] [INFO] system - some information message.
[2016-07-28 23:24:27.947] [DEBUG] system - some debug message.
colored
[2016-07-28 23:25:00.391] [FATAL] system - some fatal error message.
[2016-07-28 23:25:00.396] [ERROR] system - some error message.
[2016-07-28 23:25:00.397] [WARN] system - some warning mesage.
[2016-07-28 23:25:00.397] [INFO] system - some information message.
[2016-07-28 23:25:00.397] [DEBUG] system - some debug message.

pattern

pattern で利用可能な パターン は以下の通りです。

パターン 説明
%r toLocaleTimeStringで得られる「時刻」
%p 「ログレベル」
%c 「ログカテゴリ」
%h 「ホスト名」
%m 「ログデータ」
%d 「日付」。以下のパターンが利用可能です。
  • %d{ISO8601}
  • %d{ISO8601_WITH_TZ_OFFSET}
  • %d{ABSOLUTE}
  • %d{DATE}
%% 「%」
%n 「改行」
%z 「pid」
%x{<トークン>} 「動的トークン」を指定します。 動的トークンの内容は tokens パラメータで定義します。
%[%] カラーブロックの定義

tokens

pattern 中で指定された 動的トークン名 をキーとし、 関数 を バリュー とするハッシュオブジェクトを指定します。 指定した文字列を返す関数は、ログ出力時に呼び出されて、該当の処理結果を出力します。


参考記事

Node.js + Express で作る Webアプリケーション 開発 入門

Node.js だけでは 実践向きではないので、 Express と EJS を組み合わせてもう少し実践で使えるようなサンプルを作ってみました。 Express は Node.js 向けの Webアプリケーション開発フレームワーク で、ミドルウェアのような存在です。 EJS は テンプレートエンジン で、Java で言うところの JSP に近い コーディング ができます。 今回はさらにフロントエンドでよく使われる jQuery、 Bootstrap を組み込んで実装しています。

実用…となるとまだまだ足りない部分がありますが、初心者向けのサンプルとしてはある程度網羅できたものになっているかと思います。


目次


概要

本記事では単純な index ページを返却する Webアプリケーションを作成します。 開発環境は サーバーサイドを node.js + Express + EJS 、クライアントサイドを jQuery、 Bootstrap が使える環境とするので、 Webアプリケーション開発の雛型になるかと思います。

利用するフレームワークおよびライブラリ

  • パッケージ管理
    • npm
    • Bower
  • サーバーサイド
    • node.js
    • Express
    • EJS
  • クライアントサイド
    • jQuery
    • Bootstrap

作成する Webアプリケーション は、「単純な画像入りトップページを表示するもの」です。 出来上がった Webアプリケーション へアクセスした際のイメージは以下のようなものです。

また、今回のプロジェクトで作成するフォルダおよびファイルの全体像は以下の通りです。

前提条件

以下のアプリがインストールされた Windows 環境

  • Node.js / npm
  • Bower
  • Git

環境は Windows で開発を行います。 Node.js に関連する上に記載のアプリは事前にインストール済みである前提で進めます。 まだインストールしていない場合は先にインストールをお願いいたします。 それぞれのインストール方法や手順については以下の記事を参考にしていただければと思います。

なお、エディタは IDE (Visual Studio や Eclipse) を利用して開発してもよいのですが…裏側で実装されたもろもろが何を示しているのか分からないのでテキストエディタで進めます。

環境作成

開発に必要なディレクトリ、パッケージを準備します。 パッケージは依存関係があるのでバージョンには注意します。

  1. プロジェクトディレクトリを作成。

    ここでは「sample」をルートディレクトリとして作成します。

  2. デフォルトのディレクトリ構成として以下のフォルダ構成を準備します。

    ディレクトリ構成

    sample
      │
      ├─public
      │  ├─images
      │  ├─javascripts
      │  ├─stylesheets
      │  └─third_party
      │
      ├─routes
      │
      └─views
    
  3. npm initpackage.json を作成。

    基本的にはすべての質問を Enter で回答すれば完成します。 以下のサンプルでは一部でデフォルト以外の回答をして作成しています。

    package.json

    {
      "name": "sample",
      "version": "1.0.0",
      "description": "",
      "main": "app.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "garafu",
      "license": "MIT"
    }
    
  4. 以下のパッケージを npm でインストール。

    • express
    • ejs

    具体的なインストール手順はそれぞれ以下の通りです。 (といっても、npm install パッケージ名 --save をコマンドで叩くだけなのですが…)

    1. express のインストール
      npm install express --save
      
    2. ejs のインストール
      npm install ejs --save
      
  5. bower initbower.json を作成。

    package.json の作成と同じく、基本的にはすべての質問を Enter で回答すれば完成します。 以下のサンプルでは一部でデフォルト以外の回答をして作成しています。

    bower.json

    {
      "name": "sample",
      "description": "",
      "main": "app.js",
      "authors": [
        "garafu"
      ],
      "license": "MIT",
      "homepage": "",
      "ignore": [
        "**/.*",
        "node_modules",
        "bower_components",
        "test",
        "tests"
      ]
    }
    
  6. .bowerrc を作成。

    作成用のコマンドは存在しないので テキストエディタ を使って作成します。 ファイル保存は念のため UTF-8 で保存しておきます。

    .bowerrc

    {
      "directory": "public/third_party"
    }
    
  7. 以下のパッケージを bower でインストール。

    • jQuery
    • Bootstrap

    具体的なインストール手順はそれぞれ以下の通りです。 (こちらも npm と似ていて bower install パッケージ名 --save をコマンドで叩くだけです)

    1. jQuery を以下のコマンドでインストール。

      jQuery は Bootstrap との依存関係があるため バージョン 2.x.x 以前を指定してインストールします。

      bower install jquery#2 --save
      
    2. Bootstrap を以下のコマンドでインストール。

      bower install bootstrap --save
      

実装

実際に作成するのは以下のファイルです。

また、以下の場所に任意の画像を置いてください。

  • ./public/images/neko.jpg

ちなみに今回は PAKUTASO に公開されている 獲物を狙ってるメス猫(スコティッシュフォールド) のフリー素材をお借りしました。

./app.js

var express = require('express');

// express の実態 Application を生成
var app = express();

// テンプレートエンジンを EJS に設定
app.set('views', './views');
app.set('view engine', 'ejs');

// 静的ファイルは無条件に公開
app.use('/public', express.static('public'));

// ルーティング設定
app.use('/', require('./routes/index.js'));

// サーバーをポート 3000 で起動
app.listen(3000);

// アプリケーション開始ログ
console.log('Server running at http://localhost:3000');

./routes/index.js

var express = require('express');
var router = express.Router();

// デフォルトルーティング
router.get('/', function (request, response) {
    response.render('index', { title: 'Sample Node.js', message: 'Hello there!' });
});

module.exports = router;

第2引数で渡されたオブジェクトはテンプレートエンジン( EJS )に渡されます。

./views/index.ejs

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><%= title %></title>
    <link rel="stylesheet" href="/public/third_party/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="/public/stylesheets/index.css" />
  </head>
  <body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#about">About</a></li>
            <li><a href="#contact">Contact</a></li>
          </ul>
        </div>
      </div>
    </nav>

    <div class="container">
      <div class="starter-template">
        <h1>Bootstrap starter template</h1>
        <p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
        <p><img src="/public/images/neko.png" class="img-thumbnail"></p>
        <p><%= message %></p>
      </div>
    </div>

    <script type="text/javascript" src="/public/third_party/jquery/dist/jquery.js"></script>
    <script type="text/javascript" src="/public/third_party/bootstrap/dist/js/bootstrap.js"></script>
 <script type="text/javascript" src="/public/javascripts/index.js"></script>
  </body>
</html>

渡されたオブジェクトは <%= プロパティ名%> で呼び出すことができます。 JSP 同様、条件判定 や ループ処理 もありますが…、今回はただ表示するだけのサンプルとしています。

./public/javascripts/index.js

var image_onclick = function (event) {
    window.alert('Hello World !');
};

var document_onready = function (event) {
    $('#img').on('click', image_onclick);
};

$(document).ready(document_onready);

./public/stylesheets/index.css

body {
  padding-top: 50px;
}
.starter-template {
  padding: 40px 15px;
  text-align: center;
}

テスト

実装まで終わりましたので、実際に動かして動作することを確認します。

  1. コマンドプロンプトを起動
  2. プロジェクトルートディレクトリへ移動
    cd /d "D:\Work\sample"
    
  3. 以下のコマンドを実行して Webアプリケーション を起動
    node app.js
    
  4. ブラウザからアクセス

    任意のブラウザから http://localhost:3000/ へアクセスします。 正常に動作していれば、以下のような画面が表示されるハズです。


以上でサンプル実装は完了となります。 多数あるパッケージやフレームワークをどのように組み合わせていけばよいかが理解できたかと思います。 これをベースに Node.js で実践的な Webアプリケーション が作られるといいな、と思っています。

参考記事

ECMAScript 2015 (ES6) まとめ (モジュール編)

ECMAScript2015 (ES6) で機能追加されたもののうち、今回は class に関連する部分をピックアップしてまとめていきます。 ついに JavaScript でも クラス が使えるようになった! …のですが、不完全な部分もありますので、この記事を読んでそんなところもピックアップしてもらえればと思います。

ES2015 に関連する まとめ記事 は以下の通りです。

関連記事


目次


ESモジュール概要

ESモジュールは ECMAScript で定義された 標準規格 です。 Node.js で定義されている モジュール とは少し異なるので注意です。 残念なのは現在(2016年6月時点)どのブラウザも実装してないような点。。

ブラウザで試せないですが、以下に簡単なサンプルコードを記載します。

module.js

export default function () {
  console.log('exported module method.');
};

main.js

import sample from './module.js';

sample();     // module.js の default function が実行される。

基本構文

import

外部定義されたモジュールファイルパスを指定してモジュールを読み込みます。 読み込まれたモジュールは指定された変数へ代入して読み込み または モジュールに定義された名称そのままで読み込み が行われます。

構文
import name from 'module';
import {member} from 'module';
import {member1 [as alias1] [, ... , menberN [as aliasN]]} from 'module';
name
デフォルトエクスポート設定されたオブジェクトを受け取る変数名
module
モジュールファイルへのパス
member1 ... memberN
モジュールファイル中に定義された名前付きエクスポートのうち、インポートしたいオブジェクト名
alias1 ... aliasN
インポートしたオブジェクトを受け取る変数名
利用例

モジュール内のすべてのコンテンツをインポート

import some from './some.js';

モジュール内のオブジェクト名を指定してインポート

import {some} from './some.js';

モジュール内のオブジェクト名を複数指定してインポート

import {hoge, foo, bar} from './some.js';

モジュール内のオブジェクト名を指定して別名にインポート

import {some as hoge} from './some.js';

export

export できるものは プリミティブ、オブジェクト、関数、クラス と何でも指定されたものをモジュールとしてエクスポートできます。

export する方法は default export と named export の2種類があります。 default export はその名の通り、デフォルトとして指定された単一オブジェクトをエクスポートできます。 named export は名前付けされたオブジェクトを複数同時にエクスポートできます。 実際のモジュールでは default export と named export を混在させることもできます。

構文

default export

export default function () {...};
export default class {...}

named export

export var name1 = ...;
export let name2 = ...;
export const name3 = ...;
export function name4 () {...}
export class name5 {...}
利用例

デフォルトで関数を出力するサンプルコード

export default function (msg) {
  console.log(msg);
}

デフォルトでクラスを出力するサンプルコード

export default class {
  id: '',
  name: '',
  say () {
    console.log('My name is ' + this.name + '.');
  }
}

名前付きでオブジェクトを出力するサンプルコード

export User = function () {
  this.id = '';
  this.name = '';
};

名前付きで定数を出力するサンプルコード

export const STATUS_X = 0x0001;
export const STATUS_Y = 0x0002;
export const STATUS_Z = 0x0004;

名前付きで複数のオブジェクトを出力するサンプルコード

const STATUS = {
  X: 0x001,
  Y: 0x002,
  Z: 0x004
};

var User = function () {
  this.Id = '';
  this.Name = '';
  this.Status = '';
};

export {STATUS, User};

ESモジュールについてまとめてみました。 ブラウザでは使えないので参考情報にしかならないですが…。。 そのうち使えるようになった時にそなえての準備になればと思います。

参考記事

ECMAScript 2015 (ES6) まとめ (クラス編)

ECMAScript2015 (ES6) で機能追加されたもののうち、今回は class に関連する部分をピックアップしてまとめていきます。 ついに JavaScript でも クラス が使えるようになった! …のですが、不完全な部分もありますので、この記事を読んでそんなところもピックアップしてもらえればと思います。

ちなみに、ES2015 に関連する まとめ記事 は以下の通りです。

関連記事


目次


JavaScript の クラス 概要

Java や C# 等の言語には最初から class キーワード が存在していますが、 JavaScript には ES2015 まで存在しませんでした。 ES2015 に対応しているブラウザから class が使えるようになります。

JavaScript において class を利用する簡単な例を以下に載せます。

// クラスを宣言
class Car {
  constructor (name) {
    this.name = name;
  }
  
  drive () {
    console.log(`drive ${this.name}.`);
    console.log('zoom zoom ...');
  }
}

// クラスをインスタンス化して利用
var mycar = new Car('ponkotsu');
mycar.drive();

クラスの宣言は class キーワード に続けて クラス名 を指定することでできます。 クラスの内容は オブジェクト 定義する記載方法と似ていますが、 メンバの区切りにカンマ不要な点が異なります。 クラスを利用する際のインスタンス化は new で行い、メソッド呼び出しは . で行うあたりはよくある記法と同じかと思います。

クラス の 定義

クラス宣言

クラス宣言を行うには class キーワード を利用します。 宣言方法は「classキーワード に引き続き クラス名 を記述する方法」と「変数として クラス定義 する方法」の2通りがあります。

構文
class ClassName {
  definitions
}
var ClassName = class [ClassNameAlias] {
  definitions
}
ClassName
クラス名を指定。インスタンス生成時に利用する名前。
ClassNameAlias
クラス内部で利用するクラス名。staticメソッドを呼び出すとき等に使う。オプションなので定義しなくても動作する。
definitions
クラスの内容を記述。

コンストラクタ

その名の通り、クラスをインスタンス化するとき呼ばれる特別なメソッドです。 名称が決まっており constructor を利用します。 メソッドになるため仮引数やメソッド本体の記載方法は通常のメソッドと同じです。 JavaScript の クラス ではメンバ変数の初期化をコンストラクタで行います。

構文
var ClassName = class {
  constructor ([param...]) {
    statements
  }
}
利用例
var Car = class {
  constructor (name) {
    console.log(`create ${name}`);
  }
}

var mycar = new Car('ponkotsu');    // "create ponkotsu"

メソッド

オブジェクトの操作を表すメソッドを定義することができます。 通常の関数と同じで関数名と仮引数を定義します。 利用する際はインスタンス化した後に定義したメソッド名を指定することで呼び出せます。 インスタンス化せずに呼び出せる staticメソッド は static キーワード を用いて定義します。

JavaScript の クラス で通常通りメソッドを追加すると public な扱いになり、どこからでもアクセスできるメソッドになります。 private なメソッドを作りたい場合は ES2015 で追加された Symbol を使うことで疑似的な表現ができると思います。 Symbol はユニークな値のため、その名称を知ったうえで意図的に Symbol を利用してアクセスしない限り メソッド アクセス できません。

構文

publicメソッド

var ClassName = class {
  MethodName ([params...]) {
    statements
  }
}

privateメソッド

var ClassName = class {
  [Symbol('MethodName')] ([params...]) {
    statements
  }
}

staticメソッド

var ClassName = class {
  static MethodName ([params...] {
    statements
  }
}
MethodName
メソッド名
params...
メソッドに渡す仮引数
statements
メソッド本体
利用例

var Car = class {
  constructor(name) {
    this.name = name;
  }

  // publicメソッド
  echo () {
    console.log(this.name);
  }

  // privateメソッド
  [Symbol.for('_echo')]() {
    console.log(`${(new Date(Date.now())).toISOString()} : ${this.name}`);
  }

  // staticメソッド
  static maintenance() {
    console.log('now maintenance...');
  }
};


var o = new Car('hoge');
o.echo();                     // "hoge"
o._echo();                    // TypeError: o._echo is not a function
o['_echo']();                 // TypeError: o._echo is not a function
o[Symbol.for('_echo')]();     // "2016-07-01T00:22:36.471Z : hoge"
Car.maintenance()             // "now maintenance..."

プロパティ

プロパティ(メンバ変数)の定義および初期化は コンストラクタ の中で行います。 プロパティに対するアクセスを setter / getter で制御したい場合は追加で必要な setter / getter を定義を行います。 setter / getter を利用する際は本体となるプロパティは private とするのが通常と思いますので、 Symbol とセットで定義になるかと思います。

構文

通常の定義

var ClassName = class {
  constructor () {
    // publicプロパティ
    this.publicMember = 'prop1';

    // privateプロパティ
    this[Symbol.for('privateMember')] = 'prop2';
  }
}

setter / getter を用いた定義

var ClassName = class {
  constructor () {
    // プロパティの実態
    this[Symbol.for('memberName')] = initialValue;
  }
  
  // getter定義
  get PropertyName () {
    // getterの内容
    return this[Symbol.for('memberName')];
  }

  // setter定義
  set PropertyName (value) {
    // setterの内容
    this[Symbol.for('memberName')] = value;
  }
}
利用例
var Car = class {
  constructor(name) {
    this.name = name;
    this[Symbol.for('created')] = (new Date(Date.now())).toISOString();
    this[Symbol.for('_number')] = Math.floor(Math.random() * 10000);
  }
  getCreatedTime() {
    console.log(this[Symbol.for('created')]);
  }
  echo() {
    console.log(`this name is "${this.name}"`);
  }
  get number() {
    return this[Symbol.for('_number')];
  }
  set number(value) {
    this[Symbol.for('_number')] = value;
  }
};


var o = new Car('hoge');
o.created;                    // undefined
o.getCreatedTime();           // 2016-07-01T01:00:22.833Z  <- インスタンス生成日時
o.echo();                     // this instance's name is "hoge"
o.number;                     // 7184  <- 乱数

継承

あらかじめ作成してあるクラスを継承して新しいクラスを作成することができます。 継承は extends キーワード を利用して親クラスを指定します。

子クラスにおいて親クラスのメソッドを呼ぶ場合、super キーワードを利用して呼び出します。 親クラスのコンストラクタを呼び出す場合は super() で呼び出します。 親クラスのメソッドを呼び出す場合は super.parentMethod() で呼び出します。

子クラスにおいて親クラスのメソッドをオーバーライド(上書き)する場合、同名のメソッドを定義することでオーバーライドできます。

構文
var ChildClassName = class [ChildClassNameAlias] extends ParentClassName {
  definitions
}
ChilClassName
子クラス名
ParentClassName
親クラス名
利用例
// 親クラスの定義
var Car = class {
  constructor(name) {
    this.name = name;
    this.created = (new Date(Date.now())).toISOString();
  }

  getTimeStamp() {
    return this.created;
  }

  drive() {
    console.log('zoom zoom ...');
  }
};

// 子クラスの定義
// Car を継承して Lamborghini を作成
var Lamborghini = class extends Car {
  constructor(name) {
    // 親コンストラクタを呼び出す例
    super(name);
  }

  echo() {
    // 親メソッドを呼び出す
    console.log('call parent class getTimeStamp() : ' + super.getTimeStamp());
  }

  // オーバーライドの例
  drive() {
    console.log(`fire ${this.name} !!`);
  }
};


var car1 = new Lamborghini('lamgorghini');
car1.getTimeStamp();    // "2016-07-01T04:14:01.131Z"
car1.echo();            // call parent class getTimeStamp() : 2016-07-01T04:14:01.131Z
car1.drive();           // fire lamgorghini !!

クラス の 利用

インスタンス化

インスタンス化は new に続いてクラス名を記述することで行えます。 インスタンス化した際は最初に コンストラクタ (constructor メソッド) が呼ばれます。 ちなみに、インスタンス破棄時に呼ばれる デストラクタ は存在しません。

構文
var instance = new ClassName();
利用例
var User = class User {
  constructor () {
    this.id = '';
    this.name = '';
  }
  login () {
    console.log('login');
  }
  logout () {
    console.log('logout');
  }
};

var user = new User();
user.login();
user.logout();

ECMAScript2015 (ES2015 / ES6) から利用できるようになった クラス についてまとめてみました。 JavaScript において クラス を利用するとき利用する setter / getter (ES5) や Symbol型 についてもあわせて記載しました。 この記事一本で JavaScript のクラス作成、利用 は出来る…ハズ!

参考記事

ECMAScript 2015 (ES6) まとめ (基本編)

ECMAScript 2015 (ECMAScript 6 / ES6 と呼ばれていたもの) は 以前の ECMAScript 5 と比べるとかなりの構文、式、機能が追加されています。 ざっくりとまとめ…としたくてもかなりの量があったので数回に分けてみていきたいと思います。

初回は JavaScript 実装する中でも コア となりそうな データ型、宣言、式と演算子、関数 に関して見ていきます。 次回以降では クラス、モジュール、組み込みオブジェクト について見ていきたいと思います。

関連記事


目次


データ型

8進数 (Octal) / 2進数 (Bynary) リテラル

8進数 と 2進数 のリテラルが使えるようになります。 8進数 は "0(ゼロ)" と "o(オー)" のプレフィックスを付けることで表現します。 "o(オー)" のプレフィックスは任意なのでなくても 8進数 として扱われます。 2進数 は "0(ゼロ)" と "b(ビー)" のプレフィックスを付けることで表現します。

構文
"0(ゼロ)" + ["o(オー)"] + "数字"  // 8進数
"0(ゼロ)" + "b(ビー)" + "数字"    // 2進数
利用例
var oval = 0o0012;  // 8進数の"12"   (= 10進数の"10")
var bval = 0b1010;  // 2進数の"1010" (= 10進数の"10")

console.log(oval);  // "10"
console.log(bval);  // "10"

テンプレートリテラル

組み込み式を利用できる文字列リテラルが定義できます。 テンプレートリテラルはバッククォート(``) で文字列を囲みます。 プレースホルダーはドル記号と波括弧(${expression})で囲みます。 改行はエスケープを行わなくてもそのまま表現できます。 テンプレートリテラルは生成時にプレースホルダ―が計算されるため、あらかじめプレースホルダーに代入する値は準備しておく必要があります。

構文
var text = `plane text ${expression}`;
利用例

複数行

// 複数行
var text = `O Romeo, Romeo! 
Wherefore art thou Romeo?`;

// 式内挿入
var a = 3;
var b = 4;
var text = '${a} + ${b} = ${a+b}';

console.log(text);   // "3 + 4 = 7"

Unicodeコードポイントエスケープ

文字列を表現する際、Unicodeコードポイントエスケープが利用できます。 "\u"のプレフィックスに続いて4桁の16進数を記載することで表現します。

構文
\uhhhh
利用例
var escaped = "\u3053\u3093\u306B\u3061\u306F";
console.log(escaped);  // "こんにちは"

オブジェクトリテラル拡張

オブジェクト生成に関して以下のようなことができるようになります。

  • プロパティ値の自動代入
  • 算出プロパティキー
  • メソッド定義の省略
利用例

プロパティ値の自動代入。

オブジェクト生成時にスコープ内でプロパティ名と同名の変数がある場合、その変数値をプロパティの値として自動代入します。

var getMessage(id, message) {
  return {
    id,
    message
  };
};
var m = getMessage(123, 'Hello World');
console.log(m.message);  // 'Hello World'

算出プロパティーキー

オブジェクト生成時に動的なキー作成ができます。

var key = 'foo'
var o = {
  [key]: 'bar'
};
console.log(o.foo);  // 'bar'

メソッド定義の略記

オブジェクトにメソッドを追加する際、function を省略できます。

var o = {
  msg: 'hello world.',
  greet() {
    return this.msg;
  }
};

正規表現 の "y" および "u" フラグ

正規表現で新しいフラグ y および u が使えるようになります。 それぞれのフラグの意味は以下のようなものになっています。

y
lastIndexで指定された位置から始まる文字列に対してマッチングを行う
u
指定されたマッチングパターンに対してUnicodeに関する機能を有効化する
利用例
var text = "1st 2nd 3rd 4th 5th";
var regexp = /\d\w\w/y

regexp.lastIndex;       // 0
text.match(regexp);     // ["1st"]
regexp.lastIndex = 2    // 2
text.match(regexp);     // null
regexp.lastIndex = 4    // 4
text.match(regexp);     // ["2nd"]

Symbol型

新しい プリミティブ型 Symbol が追加されます。 Symbol は 必ず ユニーク な 値 をとる型です。

構文
var value = Symbol([name]);
name
シンボルを説明する文字列。
利用例

Symbol型 は同じ文字列で生成しても違う値とみなされます。 完全に同じ値を利用したい場合、 Symbol.for メソッド を利用して Symbol型 の生成および利用を行います。

var sym1 = Symbol('key1');
var sym2 = Symbol('key1');

console.log(sym1 === sym2);    // false

var sym3 = Symbol.for('key2');
var sym4 = Symbol.for('key2');

console.log(sym3 === sym4);    // true

宣言

const

読み取り専用の定数を生成できます。 読み取り専用のため、再設定することができません。

有効なスコープは宣言されたグローバルまたは関数内になります。

構文
const name = value;
name
定数名
value
定数値
利用例
const MESSAGE = 'Hello World.';
console.log(MESSAGE);        // "Hello World."

MESSAGE = 'Hello World !!';  // <-エラーになる

let

ブロックスコープ内で有効な変数を宣言できます。 宣言された変数値はブロック内だけで利用できます。

構文
let name = value;
name
変数名
value
変数値
利用例
var msg = 'Hello';

{
  // この範囲はブロックスコープ
  let msg = 'こんにちは';
  console.log(msg);    // "こんにちは"
}

console.log(msg);      // "Hello"

波括弧 ({}) で囲まれた範囲がブロックスコープとなるため、その範囲内だけが有効範囲となります。 ブロックスコープを抜けるとブロックスコープ内の内容は破棄されるため、もとのスコープをたどって得られる値が表示されます。

var arr = [1, 2, 3, 4, 5];
for (let i = arr.length; i--;) {
  console.log(i);
}

変数 i はブロックスコープとして forループ の内部だけで有効な変数となります。

式と演算子

分割代入(Destructing assignment)

配列またはオブジェクトから任意のプロパティ値を取り出して代入する方法です。

構文
[a, b] = [1, 2];
({a, b} = {a:1, b:2});

オブジェクトへ分割代入する際は 式 として実行するために 丸括弧 でくくります。

利用例

単純な初期化

var a, b;

// 配列を使った例
[a, b] = [1, 2];

// オブジェクトを使った例
({a, b} = {a:1, b:2});

console.log(a);    // 1
console.log(b);    // 2

デフォルト値

var a, b, c, d;

// 配列を使った例
[a=1, b=2, c=3, d=4] = [, ,5, 10];

// オブジェクトを使った例
({a=1, b=2, c=3, d=4} = {c:5, d:10});

console.log(a);    // 1
console.log(b);    // 2
console.log(c);    // 5
console.log(d);    // 10

スワップ

var a = 5, b = 8;
[a, b] = [b, a];

console.log(a);    // 8
console.log(b);    // 5

必要な値の取り出し

var url = "http://garafu.blogspot.jp/2015/06/regexp-sample.html";
var regexp = /^(\w+)\:\/\/([^\/]+)\/(.*)$/;
var [,protocol, host, fullpath] = regexp.exec(url);

console.log(protocol);    // "http"
console.log(host);        // "garafu.blogspot.jp"
console.log(fullpath);    // "2015/06/regexp-sample.html"
var name, age;

var getUser = function (id) {
  var name, kana, age, status;
  
  // 何か処理
  name = 'Yoko Nishikawa';
  age = 16;
  
  return {
    id,
    name,
    kana,
    age,
    status
  };
};

var {name, age} = getUser('0123');
console.log(name);      // "Yoko Nishikawa"
console.log(age);       // 16

スプレッド演算子 (Spread Operator)

配列が設定されている変数名に ... のプレフィックスを付けることで、複数の引数、複数の配列要素、複数の値を利用する場所で式が展開されます。

構文
...arg
利用例

関数の引数

var helloFunc = function (x, y, z) { };
var args = [1, 2, 3];
helloFunc(...args);

配列リテラル生成

var parts = [2, 3];
var full = [1, ...parts, 4, 5];
console.log(full); // "[1, 2, 3, 4, 5]"

分割代入

var [a, b, ...arr] = [1, 2, 3, 4, 5];
console.log(a);    // "1"
console.log(b);    // "2"
console.log(c);    // "[3, 4, 5]"

配列の結合

var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
arr1.push(...arr2);

for ... of 文

カウント可能なオブジェクト(Array, Map, Set, String, TypedArray, DOMElementsTree, arguments) の明らかな要素のみを順に捜査します。 for ... in はプロパティ含めてすべての値を列挙するが for ... of は配列の要素のみを列挙します。

構文
for (variable of iterable) {
  statement
}
variable
要素を代入する変数
iterable
カウント可能なオブジェクト
statement
処理
利用例
var arr = [1, 2, 3];
arr.message = 'Hello World';

for (let item of arr) {
  console.log(item);
};
// -> 1, 2, 3
// 'Hello World'; は表示されない

関数

デフォルト引数 (Default Parameters)

関数の仮引数にデフォルト値を設定することができます。 デフォルト引数は呼び出し時に評価されるので、オブジェクトは呼び出される度に生成されます。

構文
function [name]([param1[=defaultValue1][, ... [, paramN[=defaultValueN]]]]){
  // 何か処理...
}
利用例
var getMessage(name = 'tanaka') {
  return 'Hello ' + name + '.';
};

var message = getMessage();

console.log(message);       // 'Hello tanaka.'

残余引数 (Rest Parameters)

関数の最後の引数に「...」の接頭辞を付けることで、不特定多数の引数を配列として受け取ります。

残余引数(Rest Prameters) と arguments の違いは以下にあります。

  • 残余引数は任意の名前を付けられる
  • 残余引数は一部の引数値になりうる
    argumentsはすべての引数を持つ
  • 残余引数はarrayインスタンス
  • argumentsは独自オブジェクト
構文
var expression = function (a, b, ...args) {
}
expression
関数名
a, b
通常の引数。
...args
残余引数。「...」のプレフィックスを仮引数名につけることで設定できる。
利用例
var log = function (...args) {
  console.log(args.join(','));
};

log("host", "status");    // "host,status"

アロー関数

C# の ラムダ式 のような記載ができます。 アロー関数 で 定義された関数は すべて 無名関数 になります。

構文

基本

(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression

引数が1個の場合、丸括弧 は 省略可能

(param) => { statements }
param => { statements }

引数が0個の場合、丸括弧 は 省略不可

() => { statements }
利用例
var hoge = function () {
  var self = this;
  this.arg = 0;

  // ES6より前の古い書き方
  window.setInterval(function () {
    self.arg += 1;
    $('#out').val(self.arg);
  }, 1000);

  // ES6の新しい書き方
  window.setInterval(() => {
    this.arg += 1;
    $('#out').val(this.arg);
  }, 1000);
};

ECMAScript2015 で 追加変更された内容の中でもコアな部分についてまとめてみました。 これだけでも今までにない JavaScript ライフ が始まる予感がしますが… まだまだ クラス や モジュール といった新機能もあります。 続きはまた別の記事にて…。

参考記事