プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

JavaScript嫌いのES6入門~環境構築編~

この記事を書いてる人のレベル

 諸君 私はJavaScriptが嫌いだ
 諸君 私はJavaScriptが嫌いだ
 諸君 私はJavaScriptが大嫌いだ

 varが嫌いだ
 オーバーロードが無いのが嫌いだ
 prototypeが嫌いだ
 thisが嫌いだ
 整数型がないのが嫌いだ
 定数がないのが嫌いだ
 ファイルスコープがないのが嫌いだ
 ブロックスコープがないのが嫌いだ

 Chromeで IEで
 Firefoxで Opera最強伝説で
 Vivaldiで Edgeで

 この世界で行われるありとあらゆるJavaScriptが大嫌いだ

 以下略

 

 というわけで、私はかつてはJavaScriptが死ぬ程嫌い、いや、憎い頃もありました。
 しかし、海外勢の美しいソースコードに感化され、今では立派にJavaScriptが大嫌いです。

var value = "value";
var flag = false;
hoge();

function hoge(){
  if(flag){// ← flagはfalseなのでこのif文は無効
    var value = "hoge";
    console.log(value);
  }
  console.log(value);// ← 問題はここ
}

 これを実行して"value"と表示させるつもりが、"undefined"と表示されてファッΣ(゚Д゚;)とかなるレベルの人間です。


 これ、変数の巻き上げとか言うエラソーな名前付いてるらしいけど、ぶっちゃけ一番最初にインタプリタ作った人が、変数保持するハッシュマップをブロックレベルで管理するの面倒くさかっただけだろ。その怠慢な実装が仕様として現代まで残ってしまったと。Verilogのwireやregと同レベルの怠慢と思っています。C88以下だぞ。真実かどうかは知らないけども。

ES6では色々解決されたらしい

 ES2015やらなんやら呼び方が色々あるのか、何がES6なのかワカリマセンが(C++1xとかC++2xみたいなもの?)、letやconstが導入され、モジュールが追加されたことでファイルスコープも出来る様です。classだって作れるらしい。

 今までJavaScriptを使うこともなかったので、JavaScriptの知識はCoffeeScriptが出始めた頃で停止している。そんな人間の屑がちょっと個人的な理由でJavaScriptを触らないといけなくなったので、ついでにES6に入門してみました。


環境構築の段階で心折れそう

 さて、何はともあれ、環境構築からかな。JavaScriptなんて簡単簡単………

f:id:nodamushi:20170924190105p:plain


 あ゛ぁ゛ん゛!?


 え、何これ、今JavaScript界隈ってこんな面倒くさいことになってるの?これが素晴らしい!みたいな雰囲気だけど、みんな本気で言ってるの?JavaScriptってもっと、こう、お手軽で簡単な物じゃなかった?

 環境構築が面倒と言われるC++より酷い。ていうか、C++なんかVisualStudio入れれば動くし、アンチVisualStudioな場合でもMinGWとMakeとCMake入れりゃ十分だというのに。ていうか、MSYS2の上でだいたい全部すむのに。一昔前のTeXの環境構築を彷彿とさせるカオス。しかも、1年単位で言ってることがコロコロ変わってる。

 CoffeeScriptの時代より悪化してる様な気が………

 何かもう頭が痛くなってきましたが、ここで脱落してしまえばいつまで経ってもCoffeeScript良いよねっていう老害に終始してしまいます。一個ずつ紐解き、耐えねば。

 CoffeeScript2が出てたぞ!!私は老害じゃなかった!わはははは!

Node.jsとnpm

 どうも、Node.jsからは逃げることは不可能らしいです。正直、Webと関わりが無い私は、こやつが一体何者かもよく分かっていません。なので、コレがなんなのかもついでに学習してみました。

 JavaScript環境だとかサーバーがとか、IOがとか、あちこちでよーわからん説明がされていますが、個人的には

  • Node.js≒MSYS
  • npm  ≒pacman

という理解で決着しました。
 要するに、JavaScriptで作られ、JavaScriptが動くシェルみたいな環境。Emacsのeshellみたいな。正確には違うのでしょうが、個人的にはコレで納得出来たので良しとします。マサカリ飛んで来そう?ははは。全部避けてやる。

 Windows上でC++開発するならMSYSが必須(異論は認める)な様に、現代のJavaScriptを開発するなら、Node.jsという環境が必要なようです。

 元からCoffeeScriptのために入っていましたが、ついでなので最新版に更新しました。

package.json

 package.jsonがあるディレクトリから下が、プロジェクト管理下に置かれるらしい。つまりは、Eclipseの.projectファイルとか、CMakeLists.txtみたいなものかな。

 とりあえず、以下の様な内容を書いておきゃいいらしい。

{
  "name":"プロジェクト名",
  "version":"1.0.0",
  "scripts": {
     "run":"node main.jsとか、実際に実行させる際のコマンド"
      //ここにビルドコマンドとか、実行コマンドとか書くと、npm run ***でそのコマンドが実行される
  }
}

コンパイラ

 ES6を試したいのですが、現状はES6が完全に動く環境は存在しないらしい。なので、ES6のコードを現在の環境でも動く様に変換してくれるコンパイラが必要となる。それがbabelという物らしい。ただ、babelはexport、importの解析まではしないので、ブラウザでexport,importを使うことが出来ない。
 そこで、browserifyが登場する。これを使うと、export、importの関係を解析し、一つのファイルにまとめてくれるとのこと。

Name Description
babel-cli ES6を今のブラウザで動作する様に変換する
babel-preset-**** ES2015とかES2016とか、言語仕様が違うのか知らないけど、それに対応する為のライブラリっぽい?
.babelrc どのpresetを使用するのかとか、babel-cliの動作を設定するファイルらしい。package.jsonに書いてもいいらしい。
browserify import,exportを解析し、一つのファイルにする。最近はwebpack?
babelify browserifyとbabelの橋渡し機能

 つまり、C++でいうところ、babelがコンパイラで、browserifyがリンカーで、babelifyはパイプという理解でよろしいでしょうか。

 なんか、最近はbrowserifyは死亡してwebpackだとか、何だかもう知らねぇよ。理解したら書き換えるかも。

 調べていると、これらのパッケージはグローバルにインストール(npm install -g)しちゃいけないと書かれている場合もある。バージョンごとによって挙動が違うから、プロジェクトごとに管理した方が良いとのことらしい。Mavenとかのビルドプラグインのバージョン指定みたいな感じかな。

 babel-preset-***をインストールすることで、ES2015やES2016といったバージョンに対応できるようになる。それらが何の違いがあるのかは知らない。C++の-std=c++1xとか、-std=c++2xみたいなものだろう。私は面倒くさいからenvを使うことにした。各ブラウザのバージョンに対応したコードを出力出来るらしい。
 どのpresetを使うのかというのをbabelに知らせる為に、.babelrcに情報を記述するらしい。
 

npm install --save-dev babel-cli babel-preset-env browserify babelify


".babelrc"

{
  "presets": ["env"]
}

 --save-devで、前述のpackage.jsonにインストールしたパッケージの情報が書き込まれる。pomにdependencyとかbuildを自動で書いてくれる様な物と理解した。

 はー、これだけでもおなかいっぱいです。他にも圧縮だとか色々話題があるけど、にわかの私はここまでで良いです。

package.jsonにビルドコマンド追加

 前述した様に、scriptsにビルドコマンドなどを登録しておくと、npm run buildなどで実行できるようになるらしいので、ビルドコマンドを書いておきます。

 とりあえず、srcフォルダにJavaScriptを配置し、binに吐き出すことにします。

{
  "name": "hoge",
  "version": "1.0.0",
  "scripts": {
    "build": "browserify src/main.js -o bin/main.js -t babelify"
  },
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-env": "^1.6.0"
    "babelify": "^7.3.0",
    "browserify": "^14.4.0"
  }
}

 (devDependenciesはnpm install --save-devで勝手に追加された物です。)

 main.js以外にもhoge.jsなどが追加されたとしても、main.jsからそのhoge.jsがimportされている限り、browserifyがその関係を解析して勝手にコンパイルしてくれるみたいなので、基準ファイル一つを指定しておけば良いみたいです。


テスト環境

 ブラウザでしか動かない物はブラウザで試す必要があるけど、全部ブラウザで試すのは辛たん。JUnitテストとか、GoogleTestみたいなことがしたいです。
 というわけで、テスト環境について調べてみると、mochaというNode.js上で動くテスト環境がある様だ。mocha単体にはアサーション機能が無いので、chaiとか、別な物を利用するらしい。面倒くさいな。

npm install -g mocha 
npm install -g chai

 これはglobalなインストールで良いのだろうか? さっきまでの話の理解だと、-gを付けちゃ駄目な気がするのだが。………まぁ、いいや。

 で、globalなインストールをした場合は環境変数NODE_PATHにそのパスを通しておかないと、require出来ないらしい。
 というわけで、PowerShellで以下を実行。

$node_module = npm root -g
[Environment]::SetEnvironmentVariable("NODE_PATH", $node_module, [EnvironmentVariableTarget]::User)

 これで、mochaを実行すると、プロジェクトのtestディレクトリにあるJavaScriptファイルが実行されるっぽい。というわけで、pacakge.jsonにtestコマンドを追加した。Node.jsだとexportとimport構文にはまだ対応してないので、browserifyを同じように通している。これだとエラーが起こった時に、何処が原因なのかわかりにくいのが難点。いや、別に、Node.js標準のrequireで書いてもいいんだけどさ………ES6の勉強してるのに何か、負けた気分じゃん………。
 ※mochaでコンパイラが指定出来るらしい。Babel6でMochaとChaiを使ったテストを書く
 babel-coreをインストールしておく。

npm install --save-dev babel-core 
{
  "name": "hoge",
  "version": "1.0.0",
  "scripts": {
    "build": "browserify src/main.js -o bin/main.js -t babelify",
     "test": "mocha --compilers js:babel-register"
  },
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-core": "^6.26.0",
    "babel-preset-env": "^1.6.0"
    "babelify": "^7.3.0",
    "browserify": "^14.4.0"
  }
}

Gulp.js

 どうやらMakeとかCMakeとかの様な物らしい。スクリプト的に書けるから、Gradleが近いかな。

 にわかの私はpackage.jsonのscriptsで十分なので放置

Emacsの環境設定

package-installで以下をインストールして、init.elに設定追加。

  • js2-mode : 神Add-on Keysnailの作者id:mooz様が作ったJavaScriptモード。そろそろ神が死にそう。
  • js-doc : JavaScript用JavaDocを簡単に書く為のid:mooz様が作ったパッケージ
  • tern : company-ternで使う。
  • company-tern : JavaScript用のcompany backend。auto-completeな人は知らない。
(use-package tern)
(use-package company-tern
  :init
  (add-to-list 'company-backends'(company-tern :with company-dabbrev-code :with company-yasnippet)))

(use-package js2-mode
  :init
  (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
  (add-hook 'js2-mode-hook 'tern-mode))

(use-package js-doc
  :init
  (setq js-doc-author "nodamushi")
  (add-hook 'js2-mode-hook
            #'(lambda ()
                (define-key js2-mode-map (kbd "C-x i d") 'js-doc-insert-function-doc)
                (define-key js2-mode-map "@" 'js-doc-insert-tag))))

 ternを動かすには、Node.js上で動くJavaScript解析サーバーを別途インストールする必要がある。

npm install -g tern

テストを動かしてみる

 あぁ、パトラッシュ、僕はもう疲れたよ。もうゴールしてもいいよね?

 というわけで、ひとまず、テストを実行出来るのか試してみる。

"src/main.js"

/**
 * 二乗するだけ。
 * @param {number} x 
 * @returns {number} x*x
 */
export function test(x){
  return x*x;
}

"test/allTest.js"

const chai = require("chai");
const assert = chai.assert;

import {test} from '../src/main';

describe('src/main.js',()=>{
  describe('test',()=>{
    it('(0)',()=>{
      assert.strictEqual(0,test(0));
    });
    it('(10)',()=>{
      assert.strictEqual(100,test(10));
    });
    it('(-10)',()=>{
      assert.strictEqual(100,test(-10));
    });
  });
});

 で、以下を実行。

mkdir test
npm run test

f:id:nodamushi:20170924210857p:plain
npm run testの実行結果

感想

 はー、疲れた。今回はここまでです。………あれ、私まだ何も入門してなくない?

 なんというか、狂った異常な言語から、普通の言語にやっとES6でなったという感じなのに、その普通になる為にこんだけ苦労しないといけないのか………。
 やっぱ、JavaScriptが嫌いです。