kindle本「速習ECMAScript6」のまとめ。
点数
78点
感想
ES2015だけに絞れこまれた内容だったが、しっかりと学ぶことができた。
2015年発売の本なので仕方がないが、現在(2020年3月)はbabelのバージョンが上がっているため、インストール方法や設定方法が変わってしまっていた。
その辺りを調べるのにかなり時間がかかってしまった。
主な内容
トランスコンパイラとPolyfillライブラリの違い
トランスコンパイラはコンパイルしてjsファイルを生成する、Polyfillは実行時に変換する。
babelを使って手動でトランスパイル
- node.jsをインストール
- babelをインストール
書籍上に書かれているものはnpmパッケージ名が古かった。
babel7からは@babel/core @babel/cli @babel/preset-envnpm init -y
npm install @babel/core @babel/cli @babel/preset-env
- babel.config.jsを作成
babel6までは.babelrcだったが、babel7からはbabel.config.jsでも設定が可能になった。
(babel.config.jsではJavaScriptでの記述が可能)module.exports = {
presets: ['@babel/preset-env']
};
- 変換
babel test_es6.js -o test.js
※変更を監視する場合は-wオプションを付ける。
※ディレクトリ単位でのコンパイルは-dオプションで出力先ディレクトリを指定するbabel .\source\ -d .\dist\ -w
Polyfillライブラリを有効化する
npmライブラリの@babel/preset-envがやってくれる。
(書籍では@babel/polyfillをインストールしているが、Babel 7.4.0で非推奨になった)
Babelがデフォルトで変換するのはJavaScriptの文法だけで、PromiseやSymbolなどの新しい組み込みオブジェクトやArrayクラスの新メソッドなどを利用するにはPolyfillライブラリ(polyfill.js)が必要になる。<script src="/node_modules/babel-polyfill/dist/polyfill.js"></script>
としたり、entrypointのjsファイルに以下を追記するとすべてのpolyfillが埋め込まれる。
import 'core-js/stable';
import 'regenerator-runtime/runtime';
ただし、上記の方法はファイルサイズが大きくなってしまうので、あまり使われない。
以下の方法で、useBuiltInsを指定するのが一般的。npm install core-js regenerator-runtime
でインストールし、babel.config.jsにuseBuiltIns: "usage"
とcorejs: 3
を記述すると、どのpolyfillが必要かどうかを考える必要がなくなり、不要なpolyfillは含まれなくなる。
(regenerator-runtimeはasyncとawaitを使うために必要なpolyfill)
module.exports = {
presets: [
[
'@babel/preset-env', {
useBuiltIns: 'usage',
corejs: 3,
},
],
],
};
※babelでPolyfillを使って変換されたソースはnode.jsのrequire文が使われているので、そのままブラウザで実行することはできないため(node.jsで実行はできる)、webpackやbrowserifyでバンドルする必要がある。
参考サイト
- https://qiita.com/shisama/items/88080011bbc69e3e620b
- https://qiita.com/soarflat/items/21b8955f992bf7d38581
- https://nansystem.com/migrate-babel-polyfill-to-core-js/#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB
webpackでバンドルする手順
インストール
npm install --save-dev webpack webpack-cli babel-loader
webpack.config.jsを作成
const path = require('path');
module.exports = {
mode: "development",
devtool: 'inline-source-map',
entry: path.resolve(__dirname, "src/test.js"),
output: {
path: path.resolve(__dirname, 'output'),
filename: 'test.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
presets: [['@babel/preset-env', {
useBuiltIns: 'usage',
corejs: 3
}]],
}
}
]
}
]
}
};
※babel-loaderのoptionsはbabel.config.jsに記述してあるなら不要。どちらかに記述してあればよい。
babel.config.jsにuseBuiltIns: ‘usage’を指定してwebpackを実行すると、chromeで実行時に「test.js:7102 Uncaught TypeError: __webpack_require__(…) is not a function」となってしまった。超ハマった。
⇒exclude: /node_modules/を追加したらうまくいった。Promissのpolyfillはbabelで変換してしまうとブラウザでエラーになるらしい。
※babelの設定はプロジェクトのルートにあるbabel.config.jsが使用される
webpackコマンドを実行
バンドルされたjsファイルが生成される。
参考サイト
- https://qiita.com/soarflat/items/28bf799f7e0335b68186
- https://chaika.hatenablog.com/entry/2018/12/14/083000
スコープの汚染を防ぐための即時関数は古い
従来のJavaScriptではグローバルスコープの汚染を防ぐ目的で即時関数が使われることがあったが、ES6ではやるべきではない。
{}で囲んでconst,letを使えばいいだけ。
タグ付きテンプレートリテラル
関数名の後ろにテンプレートリテラルを書いて関数を実行することができる。
※あまり使う機会はなさそうmyFunc `aaa${100}bbb${200}ccc`
だと第一引数にテンプレート文字列配列、第2引数に可変長引数で埋め込まれた変数配列、が渡される。 myFunc(`aaa${100}bbb${200}ccc`)
だと普通に文字列が渡される。
Symbol型
ユニークで不変なデータを定義することができる。
それ自身とのみ等しく、同じSymbolは2度と作ることはできない。
オブジェクトではないのでnew演算子を使うとエラーになる。
※あまり使う機会はなさそう
let hoge = Symbol();
let hoge2 = Symbol();
// hoge === hoge2はfalse
例えば定数定義でconst LOG_LEVEL_INFO = 1
としている場合、if ($logLevel === LOG_LEVEL_INFO)
という条件はif ($logLevel === 1)
と書くこともできてしまう。シンボルを使うとif ($logLevel === LOG_LEVEL_INFO)
と書くしかない、というメリットがある。
分割代入
配列・オブジェクトから個々の要素を抽出する。
…演算子を利用すると、残りの要素がまとめて配列として代入される。
let [hoge, foo] = [15, 20];
let [hoge, foo, ...others] = [15, 20, 1, 39, 55];
// 以下のように、配列を返す関数から個々の変数へ値を代入することができる。
let [hoge, foo] = (function(){return [200, 300];})();
// 以下のようにすると、2つの変数の中身が入れ替わる。
[hoge, foo] = [foo, hoge];
オブジェクトからの分割代入は、変数名をキー名と同じにしなければならない(違う名前にすることもできるが、わかりづらくなる)
順番は関係ない。let {area, name} = {name: 'マイケル', area: '東京'};
オブジェクトからの分割代入は変数宣言と同時に行った方がよい。
宣言済みの変数へオブジェクトから分割代入する場合は、{}がブロックとみなされないように()で囲む必要がある。 let area, name; ({area, name} = {name: '
マイケル', area: '東京'});
関数の仮引数にオブジェクトの分割代入を使うと、関数内で個々の変数としてアクセスすることができる。
function hoge({name, area}) {
console.log(`${name}: ${area}`);
}
hoge({name: 'マイケル', area: '埼玉'});
展開(スプレッド)演算子
「…配列」「…オブジェクト」とすると中身が展開される。
const arr1 = [1, 2];
const arr2 = [...arr1]; // [1, 2]
const arr3 = [...arr1, 3, 4]; // [1, 2, 3, 4]
// 配列のマージが以下のように簡単になる
conat arr4 = [...arr1, ...arr2];
for of
配列や列挙可能オブジェクト(NodeListなど)の要素を取り出すことができる。
配列のループにはfor ofを使うべき。配列のインデックスが必要な場合はforEach、それ以外はfor ofとする。
Mapオブジェクト
Objectとの違い「文字列以外をキーにできる」「サイズを.sizeで取得できる」「for ofを使える」というメリットがある。
const map = new Map();
map.set('name', '山田');
map.set('prefName', '埼玉');
map.set('age', '55');
console.log(map.get('name')); // 山田
console.log(map.size); // 3
console.log(map.has('name')); // true
console.log(map.keys()); // name, prefName, age(Iteratorオブジェクト)
console.log(map.keys().next().value); // name(先頭のキー=配列ではないので[0]では取得できない)
console.log(map.values()); // 山田, 埼玉, 55(Iteratorオブジェクト)
for (let [key, value] of map) {
console.log(`${key}:${value}`); // key, valueに分割代入される
}
map.delete('prefName');
map.clear(); // すべて削除
Setオブジェクト
値が重複しない配列みたいなもの。
const set = new Set();
set.add(100);
set.add(100);
set.add(50);
console.log(set.size); // 2
for (let value of set) {
console.log(value);
}
Arrayオブジェクトに追加された主なメソッド
from(), keys(), values(), entries()
プロパティ省略記法
オブジェクトのプロパティ名と同じ名前の変数は、値の指定を省略できるようになった。
const title = 'hoge';
const obj = { title }; // { "title": "hoge" }
functionキーワードの省略
メソッド定義の「function」を省略できるようになった。
const obj = {
myFunc() {
console.log('myFunc!');
}
}
class
class定義ができるようになった。
ただし、publicなどのアクセス修飾子はない。static修飾子はある。
コンストラクタはconstructor(){}で定義。
主な注意点
- サブクラスのコンストラクタではsuper();をコールしないとエラーになる
(javaのような親コンストラクタの暗黙的な呼び出しは行われない) - クラス内ではメソッドしか定義できない。
- プロパティはコンストラクタで定義するか、ゲッター/セッターで定義する。
// ゲッター
get x() {
return this._x;
}
ジェネレーター
イテレータを返す関数を定義することができる。
functionの後に*をつけるとジェネレータ関数となる。戻り値はfor ofでループさせたりnextで次の値を取得したりできる。
※使う機会はなさそう
function* myGenerator() {
yield 'A';//1回目のnext関数で返す
yield 'B';//2回目のnext関数で返す
yield 'C';//3回目のnext関数で返す
}
モジュール
import, exportが使えるようになった。
- import文で指定するファイルは拡張子を省略してもよい
- ブラウザで実行する場合はscriptタグに
type="module"
が必要 - node.jsで実行する場合は「import,exportを使っているファイルの拡張子を.mjsにする」「実行時に–experimental-modulesオプションを付ける」をしないとエラーとなる。
- デフォルトエクスポート
export default 'hoge';
でエクスポート。import name from './export';
でインポート。 - 名前付きエクスポート
export const name = 'hoge';
export const age = 39;
でエクスポート。import { name, age } from './export';
でインポート。 - デフォルトエクスポートが推奨されている
なぜなら、簡潔な方がいいから。
export default { name: 'okada' };
import person from './export';
console.log(person.name);
実行コマンド「node --experimental-modules import.mjs
」
babelは内部的にimport命令をnode.jsのrequire関数に変換する。
require関数をブラウザで動作させるにはwebpackやbrowserifyでさらに変換する必要がある。
以下、browserifyの実行方法(Babelify=BabelをBrowserifyで使えるようにする為のライブラリ)npm install -g browserify
npm install --save-dev babelify
browserify import.js export.js -t [ babelify --presets es2015 ] -o bundle.js