2016年3月28日月曜日

ECMAScript2015 (ES6) のまとめ / class, import, export

mainpicture

あと2回でES6に関しては終了です。次回はdockerかpythonによる機会学習あたりのネタを書こうかな。haskellでもいいな。IoTネタも少し書きたい。

前々回はES6に追加されたシンタックスシュガーや変数宣言の方法、前回はパラメータの渡し方や展開の新しい方法についてまとめました。

今回は、いよいよES6のメインかもしれないClassとモジュールについて取り上げます。

より詳細を確認したい方はECMAScript2015の公式を確認すると良いかと。


ECMAScript2015のまとめ

ES5と比較した時のES6の特徴としては以下のものが挙げられます。(言語仕様を読むと他にもいろいろと定義はされています)

  • let
  • template string
  • arrow function ( => )
  • default parameter ( function(x, y=10) { )
  • spread operator ( ...[1,2,3] )
  • rest parameter ( function(x, y, ...z) { )
  • class
  • import, export(今日はここまで)
  • Promise
  • Set, Map

今回はES6におけるclassとそれらを含むmodule importやexportをまとめます。


class

ES5以前でも同様のclassのようなものを作ることはできました。しかし、イマイチ読みづらく書きづらい。prototypeの理解をしないと少しイメージがしずらかったり、スコープを限定して書くこともやりにくく野良化しやすいです。

実例を出した方が理解が早いですね。まずはES5 ver。

// ES5

var User = function(name, mail) {
  this.name = name;
  this.mail = mail;
};

User.prototype.getName = function() {
  return this.name
}

var user1 = new User('Test User1', 'test1@test');
var user2 = new User('Test User2', 'test2@test');
console.log(user1.getName()); // Test User1
console.log(user2.getName()); // Test User2

// 後から宣言も可能
User.prototype.getMail = function() {
  return this.mail
}

console.log(user1.getMail()); // test1@test
console.log(user2.getMail()); // test2@test

// overrideも出来る
user1.getName = function() {
  return "<<" + this.name + ">>";
}
console.log(user1.getName()); // <<Test User1>>
console.log(user2.getName()); // Test User2

このように割と自由度高くどこでも再定義が可能で、prototypeを使ってmethodを定義するという形になります。(prototypeに関しては他の記述に任せますが、すべてのFunctionObjectの雛形のようなもので、newで生成したinstanceのクラスのbaseになるものです。)

さて上記例をどうようにES6で書き換えると次のようになります。

// ES6

class User {
  constructor(name, mail) {
    this.name = name;
    this.mail = mail;
  }

  getName() {
    return this.name;
  }
}

var user1 = new User('Test User1', 'test1@test');
var user2 = new User('Test User2', 'test2@test');

console.log(user1.getName());
console.log(user2.getName());

// これももちろんできるが、クラスに書くべし
User.prototype.getMail = function() {
  return this.mail
}

console.log(user1.getMail());
console.log(user2.getMail());

user1.getName = function() {
  return "<<" + this.name + ">>";
}
console.log(user1.getName());
console.log(user2.getName());

これで他の言語と同様にclassを定義することができるようになりました。

とはいえそれでも自由度の高い言語であることは変わらず、ダッグタイピングも可能ですし、そこでどんな定義をしても変数を突っ込んでも動かそうと思えば動かすことができます。

個人的にはthisあたりも冗長であることや、javascriptのthisは少し混乱を招きやすいのでthisは書きたくないなと感じています。


import, export

さて、Classを使えばある程度まとまった形でプログラムを書くことができるようになります。しかし、javascriptは依存関係の解決がとても難しい言語でした。htmlのhead内とかに記載しているため、どこでどの関数、変数が定義されているのか、参照しているソースコードはどれなのかとても追いにくく、関数名のバッティング等にも気をつける必要がありました。

しかし、ES6には標準でmodule化、importやexportの仕組みが用意され、お互いの依存関係を容易に示すことが可能になりました。

nodejsではrequireとmodule.exportで記載されていますが、ほぼ同様のことができるようになっています。

さきほどのUserクラスの簡易版をmodule化し、他のコードから読み込む場合は以下のようになります。

// lib.js

'use strict';

class User {
  constructor(name, mail) {
    this.name = name;
    this.mail = mail;
  }
  getName() {
    return this.name;
  }
}

export default User;
// main.js
'use strict'

import User from './lib';

var u = new User('hogehoge', 'hogehoge@hoge');
console.log(u.getName());

このような形になります。

自作のlibを参照する際には./で始めると相対パスで依存関係を解決しにいきます。npmでインストールしたlibなどはimport _ from 'lodash'import async from 'async'などのようにそのままimportすれば利用可能になります。

nodeの場合はimport、exportの記法が多少異なる( requireとかになる)のでご注意ください。あくまでもES6の標準です。

これで依存関係の把握が容易になり、global汚染もかなり解決されるようになりました。


私はできるだけ短く、できるだけ簡単にソースが書けるように頑張っております。すっきり書けるととても面白いものです。

さて、次回はES6に関しては最後になります。取り上げるのはES6で組み込まれたpromiseとset, mapに関してです。promiseは少し独特な動きをするので慣れるまで大変かもしれませんが、結構便利なものです。読んでいただければと思います。

では良いインプットと良いプログラミングを。

0 件のコメント:

コメントを投稿