夏休みを頂いて少し時間ができたので、最近気になっている ECMAScript 6 (ECMAScript 2015) に関する記事が掲載されている WEB+DB PRESS Vol.87 などを読み、実際にコードを書いてみました。 Promise による非同期処理を体験したくて手元で自由に使える Movable Type の DataAPI を使ったため、タイトルが「 Movable Type の DataAPI を使って」となった訳です。
実際に書いたコード... MT に登録されている記事を呼び出して表示するコードを示し、 ECMAScript 6 の新しい構文などについて触れてみたいと思います。
※上記実行結果は、個人ブログに書いた記事タイトルなどです。
サンプルコード
スクリプト(本体)
function getURL(url) {
return new Promise((resolve, reject) => {
var req = new XMLHttpRequest();
req.open("GET", url);
req.onload = () => {
if (req.status === 200) {
resolve(req.response);
} else {
reject(new Error(req.statusText));
}
};
req.onerror = () => {
reject(new Error("Network Error"));
};
req.send();
});
}
function apiURL(version = "v2") {
const CGI_PATH = "http://path/to/mt/mt-data-api.cgi";
return CGI_PATH + "/" + version;
}
getURL(apiURL() + "/sites/1/entries").then(response => {
var data = JSON.parse(response);
var result = "";
var target = document.getElementById("result");
data.items.forEach(item => {
let title = item.title;
let permalink = item.permalink;
result += html`<li><a href="${permalink}">${title}</a></li>`; // escapeが必要
});
target.insertAdjacentHTML("beforeend", "<ul>" + result + "</ul>");
});
スクリプト(ユーティリティ)
// https://css-tricks.com/snippets/javascript/htmlentities-for-javascript/
function htmlEntities(str) {
return String(str).replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
// http://updates.html5rocks.com/2015/01/ES6-Template-Strings
function html(pieces /*, embedValue1, embedValue2, ... */) {
var result = pieces[0];
var substitutions = [].slice.call(arguments, 1);
for (let i = 0, nSubstitutions = substitutions.length; i < nSubstitutions; i += 1) {
result += htmlEntities(substitutions[i]) + pieces[i + 1];
}
return result;
}
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ECMAScript 6 Sample</title>
<script src="js/get_entries.js" type="application/javascript;version=1.7"></script>
</head>
<body>
<div id="result">
</div><!-- /#result -->
</body>
</html>
ECMAScript 6 の構文の解説
Promise
Promise
は非同期処理のインターフェースです。 HTTP リクエストのようにすぐに結果が返らず、いずれ結果が返る処理を利用するような場合に使用します。(各所のサンプルでもよく XMLHttpRequest()
が取り上げられています。)
今回のサンプルでは、次のように getURL()
で MT に登録された記事へアクセスし、結果が返り次第 HTML を生成するようなコードになっています。
getURL(apiURL() + "/sites/1/entries").then(response => {
// 記事データ取得後に HTML を生成するコード
});
Promise
を使うことによってコードのネストが減り、見通しも良くなりますね。(説明が難しい...。)
アロー関数
従来関数式を定義する際は function
キーワードを利用していましたが、 =>
のような記号で関数式が定義できるようになりました。 function
の後ろに記述していた引数は、 =>
の前で (a, b)
のように列挙します。また、引数が一つの場合は括弧が省略できます。
サンプルコードでも、次のように随所で使われていました。
function getURL(url) {
return new Promise((resolve, reject) => {
// 略
req.onerror = () => {
reject(new Error("Network Error"));
};
// 略
});
}
getURL(apiURL() + "/sites/1/entries").then(response => {
// 略
});
function
と書かないだけですが、少し楽になる気がします。また var self = this;
のような記述も不要になるのですが、今回のコードでは使用していないので、機会があれば取り上げてみたいと思います。
関数の引数のデフォルト値・定数の宣言
関数の引数のデフォルト値が設定できるようになりました。今まで度々使いたいことがあったので、嬉しい構文です。
サンプルコードでは、 function apiURL(version = "v2")
とデフォルト値を定義しています。 MT の API は現在バージョン1とバージョン2があり、特に指定しない場合はバージョン2にアクセスするように設定しました。
また、 const CGI_PATH = "http://path/to/mt/mt-data-api.cgi";
のように const
キーワードを使用し、上書きなどができない定数を宣言しました。
function apiURL(version = "v2") {
const CGI_PATH = "http://path/to/mt/mt-data-api.cgi";
return CGI_PATH + "/" + version;
}
ブロックスコープの変数を宣言
関数内で var
キーワードを使用して変数を宣言すると、関数内でどこでも変数にアクセスできました。今回新しく導入された let
キーワードを使うと、ブロック内だけで有効な変数が宣言できるようになりました。 for
文などで使うと便利かな、などと考えています。
function html(pieces /*, embedValue1, embedValue2, ... */) {
// 略
for (let i = 0, nSubstitutions = substitutions.length; i < nSubstitutions; i += 1) {
result += htmlEntities(substitutions[i]) + pieces[i + 1];
}
return result;
}
テンプレート文字列
バッククォート( `
)で囲んだ文字列に ${}
のような書式で任意の式や変数を記述できるようになりました。サンプルコードでは、次のように記述して URL と記事タイトルを生成するようにしています。
なお、今回はバッククォートの前に html
と記述し、 html 関数によって文字列がエスケープされるように処理を行ってています。
テンプレート文字列を使うことで、文字列の生成がしやすくなったほか、 HTML を生成する場合には "
が使いやすくなるなと感じました。
getURL(apiURL() + "/sites/1/entries").then(response => {
// 略
data.items.forEach(item => {
let title = item.title;
let permalink = item.permalink;
result += html`<li><a href="${permalink}">${title}</a></li>`;
});
// 略
});
(余談) HTML をパースして DOM に追加したい
テンプレート文字列で HTML を生成したものの、 jQuery を使用していないのでどうしようかと探ったのですが、 insertAdjacentHTML()
を利用すると、 jQuery の append()
・ prepend()
・ before()
・ after()
相当のことができて便利なことを知りました。
target.insertAdjacentHTML("beforeend", "<ul>" + result + "</ul>");
まとめ
まだ深いところまで触ることはできていませんが、 ES6 の基本を体験することができ、さらに興味が湧いてきました。今回試すことができなかったモジュール管理システム等も使ってみたいなと考えています。
ちなみに、紹介したコードはトランスパイラやポリフィルを使わなくても Firefox で動作しました。(ただし、 let
を使用するために、スクリプトの読み込み時に type="application/javascript;version=1.7"
という記述を使用しました。)
MT も開発者ライセンス や Movable Type for AWS があり、気軽に利用することができます。ぜひ試してみて下さい。もちろん他にいろいろと公開されている API でも、 ES6 の新しい構文を体験できます。