Ⅳ. ES6によるBotの実装

0. ES6とは

そもそもESとは

ESとはECMAScriptの略称で、標準化団体のEcma Internationalによって標準化された言語仕様です。
Webブラウザ上で動くJavaScriptは基本的に、このECMAScriptの仕様をもとに実装されています。

ECMAScript 6

ECMAScript 6 (公式名称: ECMAScript 2015)は、2015年6月17日に採択された最新のECMAScriptで、多くの新機能が追加されています。
なお、次期バージョンのECMAScript 7 の策定も開始されています。

各ベンダのECMAScriptの対応状況

https://kangax.github.io/compat-table/es6/ compat-table_es6

トランスパイル

ES6のソースをES5などのソースに変換すること。
トランスパイルを行うツールをトランスパイラーと呼ぶ。
トランスパイラーにはBabeltraceure-compilerなどがある。

1. ES6の新機能をちょこっと紹介

詳しく知りたい方は以下などを参照してください。

1.1. let, const

let により変数を宣言することでブロックスコープが作れる。

// ES5
if (true) {
  var i = 1;
}
console.log(i); // 1
// ES6
if (true) {
  let i = 1;
}
console.log(i); // ERROR

const により変数を宣言することで、値の再代入が不可能な変数(定数)を定義できる。

const HOGE = 'hoge';
HOGE = 'fuga'; // ERROR

1.2. class構文

JavaScriptはプロトタイプベースのオブジェクト指向言語なので、今までクラスという概念はありませんでしたが、クラスベースの方が理解しやすいため、ES6からclass構文が取り入れられました。

class Human {
  // コンストラクタ作る際には必ずconstructorメソッドを利用する。
  // また、1つのクラスに 1つしか定義できない。
  constructor(name) {
    this.name = name;
  }

  // メソッド
  hello() {
    console.log('My name is ' + this.name);
  }

  // static キーワードによってクラスメソッドを定義できる
  static num_of_hands() {
    console.log(2)
  }
}

1.3. 関数

1.3.1 アロー関数

無名関数を定義する際、従来の「function」で定義していた関数を「=>」で簡易的に定義することができるようになりました。

ただし、functionで定義された関数と動作が異なる点もあるので、注意が必要。

cf. http://analogic.jp/arrow-function/#immediate-function

従来のfunctionによる関数定義

// ES5
function (arg1, arg2) {
  // 処理
}

アロー関数 基本形

// ES6
(arg1, arg2) => {
  // 処理
}

アロー関数 その2

// 処理が一行で戻り値の場合、処理を囲む{}とreturnを省略できる
(num1 + num2) => num1 + num2
/*
以下と同じ
(num1, num2) => {
  return num1 + num2;
}
*/

アロー関数 その3

// 引数が1つの場合、()を省略できる
arg => {
  // 処理
}

即時実行

((arg1, arg2) => {
  // 処理
})();

1.3.2 引数のデフォルト値

let func = (arg = 'default') => {
  console.log(arg);
};

func(); // default
func(undefined); // default
func(null); // null
func('no default'); // no default

余談ですが、JavaScriptは引数が定義されている関数を引数なしで呼び出せます。

1.3.3 可変長引数

let func = (...args) => {
  // 引数argsはArrayオブジェクト(配列)として扱われる
  for(let val of args) {
    console.log(val);
  }
}

func('hoge', 'fuga', 'piyo');
// hoge
// fuga
// piyo

※ しれっと for...of 使ってますが、ES6から追加された値を列挙する機能です。

ES5にも for...in という似たような構文がありますが、こちらは値ではなくプロパティの列挙で、prototypeチェーンまで遡って列挙する点で異なります。

1.4. テンプレート文字列

' (シングルクォーテーション)や" (ダブルクォーテーション)の代わりに` (バッククォーテーション)を使うことで、テンプレート文字列を扱えます。

変数の埋め込み

let name = 'makoto';

console.log(`Hello, ${name}!!`); // Hello, makoto!!
// 従来の場合、以下のように文字連結するしかなかった
console.log('Hello, ' + name + '!!');

改行の表現

let lines = `line1
line2
line3`;

console.log(lines);
// line1
// line2
// line3

// 従来の場合、エスケープシーケンス'\n'でしか表現できなかった。
lines = 'line1\nline2\nline3'; // 直感的に分かりにくい

1.5. 分割代入

配列やオブジェクトを要素分割して、一挙に代入することができるようになりました

配列の分割代入

let array = [1, 2, 3];

let [hoge, fuga, piyo] = array;
console.log(hoge, fuga, piyo);
// 1
// 2
// 3

/* 従来の場合
var hoge = array[0],
    fuga = array[1],
    piyo = array[2];
*/

オブジェクトの分割代入

let obj = { hoge: 1, subObj: { fuga: 2, piyo: 3 } };

let { hoge, subObj, subObj: { fuga, piyo } } = obj;
console.log(hoge, fuga, piyo, subObj);
// 1
// 2
// 3
// [object Object] {
//  fuga: 2,
//  piyo: 3
//}

/* 従来の場合
var hoge = obj.hoge,
    fuga = obj.subObj.fuga,
    piyo = obj.subObj.piyo,
    subObj = obj.subObj;
*/

分割代入の活用例

// ipアドレスの分割

let ipaddr = '192.168.10.200';

let pattern = /^(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
let [whole, oct1, oct2, oct3, oct4] = pattern.exec(ipaddr);
console.log(whole); // 192.168.10.200
console.log(oct1); // 192
console.log(oct2); // 168
console.log(oct3); // 10
console.log(oct4); // 200

1.6. Map

// Mapのインスタンス生成
let map = new Map();

// 要素の設定
map.set('hoge', 'ほげ');
map.set('fuga', 'ふが');
map.set('piyo', 'ぴよ');
map.set(0, 'ゼロ');

// 要素の参照
console.log(map.get('hoge')); // hoge

// キーの存在判定
console.log(map.has(0)); // ture
console.log(map.has('0')); // false

// 要素の数
console.log(map.size); // 4

// キーの列挙
for(let key of map.keys()) {
  console.log(key);
}
// hoge
// fuga
// piyo
// 0

// 値の列挙
for(let key of map.values()) {
  console.log(key);
}
// ほげ
// ふが
// ぴよ
// ゼロ

// キーおよび値の列挙
for(let [key, value] of map) {
  console.log(`key=${key}, value=${value}`);
}
// key=hoge, value=ほげ
// key=fuga, value=ふが
// key=piyo, value=ぴよ
// key=0, value=ゼロ

// キーの削除
map.delete(0);
for(let [key, value] of map) {
  console.log(`key=${key}, value=${value}`);
}
// key=hoge, value=ほげ
// key=fuga, value=ふが
// key=piyo, value=ぴよ

// 要素の全削除
map.clear();
for(let [key, value] of map) {
  console.log(`key=${key}, value=${value}`);
}

※ 連想配列(オブジェクト)との違い

  • 連想配列

    • キーに利用できるのは文字列のみ
    • 要素数の取得が面倒
  • Map

    • 文字列以外もキーとして扱える
    • sizeプロパティで容易に要素数が取得できる

1.7. Set

// Setのインスタンス生成
let set = new Set();

// 要素の追加
set.add(0);
set.add(1);
set.add(2);
set.add('0');
set.add(1); // 重複する値は追加されない

// 要素数の取得
console.log(set.size); // 4

// 要素の存在を確認
console.log(set.has('0')); // true
console.log(set.has('1')); // false
console.log(set.has(1)); // true


// 要素の列挙
for(let value of set) {
  console.log(value);
}
// 0
// 1
// 2
// "0"

// 要素の削除
set.delete(2);
for(let value of set) {
  console.log(value);
}
// 0
// 1
// "0"

// 要素の全削除
set.clear();
for(let value of set) {
  console.log(value);
}

1.8 import / export

ES5までは言語仕様としてモジュール化をサポートしていませんでしたが、ES6ではモジュール化をサポートしています

moduelA.js

// モジュールとして利用したい関数(変数、クラス)にexportキーワードを付与する
export function add(num1, num2) {
  return num1 + num2;
}

export function sub(num1, num2) {
  return num1 - num2;
}

// このメソッドはexportしていないので、モジュール外部から参照不可
fuction func () {
  // ...
}

moduleB.js

export const HOGE = 'hoge';
export const FUGA = 'fuga';
const PIYO = 'piyo'; // この定数はexportしていないので、モジュール外部から参照不可

main.js

import {add, sub} form 'moduleA';
import {HOGE, FUGA} form 'moduleB';

console.log(add(1, 1)); // 2
console.log(sub(1, 1)); // 0
console.log(HOGE); // HOGE
console.log(FUGA); // FUGA

モジュールの全てimportする場合

import * as alias from 'moduleA';

console.log(alias.add(1, 1)); // 2
console.log(alias.sub(1, 1)); // 0

2. babelによるトランスパイル

babelによるトランスパイルは以下のコマンドで行う

babel --presets [出力形式] [src path] -o [output path]

例)ES6で書かれたscripts/es6/es6.jsをes5に変換して、scripts/es5.jsとして出力

babel --presets es2015 scripts/es6/es5 -o scripts/es5.js

トランスパイルについて注意すること

トランスパイルはあくまで、ES6の機能をES5で実現しているだけなので、ES5の言語仕様では実現できないES6の機能があることは留意しておく必要があります。

cf. http://azu.github.io/slide/nodejs-es6/how-to-learn.html

以下はトランスパイラでは実現できない機能の一部

  • SubClass
  • Proxy
  • Symbol
  • etc...

results matching ""

    No results matching ""