スタッフブログ Staff Blog

夏の自由研究: Movable Type の DataAPI を使って体験する ECMAScript 6

夏休みを頂いて少し時間ができたので、最近気になっている ECMAScript 6 (ECMAScript 2015) に関する記事が掲載されている WEB+DB PRESS Vol.87 などを読み、実際にコードを書いてみました。 Promise による非同期処理を体験したくて手元で自由に使える Movable TypeDataAPI を使ったため、タイトルが「 Movable Type の DataAPI を使って」となった訳です。

実際に書いたコード... MT に登録されている記事を呼び出して表示するコードを示し、 ECMAScript 6 の新しい構文などについて触れてみたいと思います。
サンプルコードの実行結果。 MT に登録されている記事が10件リスト表示されている。

※上記実行結果は、個人ブログに書いた記事タイトルなどです。

サンプルコード

スクリプト(本体)

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, '&amp;')
                      .replace(/</g, '&lt;')
                      .replace(/>/g, '&gt;')
                      .replace(/"/g, '&quot;');
}

// 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 の新しい構文を体験できます。

参考書籍・サイト

お問い合わせ Contact

制作のご依頼、ご相談などは下記のフォームからご連絡ください。