Gaiax Group Advent Calendar 2018 7日目の記事です。
お疲れ様です ボクの時計ではまだ12月7日です。嘘ですすいません。
ココ最近のフロント界隈はReactやらVue.jsやらNuxt.jsやら色々流行ってますが、そもそもサーバーサイドでたまにjQuery触ってたような人間からすると「これ俺の知ってるJavaScriptじゃないよ!」っていう状態だったりします。
なのでES6の変更点についてをまとめた記事はたくさんあるんですが、
最近のフロントエンドのコードを読むうえで個人的にポイントなところをいくつか挙げてみました。
アロー関数
見た目的に一番インパクトがあるのがこれで
なんかCoffeeScriptっぽくこう書けるよっていうやつです。
// 従来の関数リテラル
const closureA = function (args){
console.log('hello');
}
// アロー関数を使った関数リテラル
const closureB = (args) => {
console.log('hello');
}
単なるfunctionの短縮形みたいです(簡単にそう説明している記事もある)が
thisを束縛しない
という重要な違いがあります。
なのでjQueryなんかでよくやるこういうのは
//functionはthisを束縛する
$('li').each(function(index){
$(this).text('list: ' + index); //thisは各li要素
});
//アロー関数はthisを束縛しない
$('li').each((index) => {
$(this).text('list: ' + index); //thisは親スコープのもの
});
という感じで意味が変わります。
functionの短縮形っていう認識だとバグを生み易いので注意です。
let宣言とブロックスコープ
以前のJavaScriptはFunctionのスコープしか無かったので即時関数とかを使ってスコープを限定してましたが、
ES6では変数の宣言にletを使うことでブロックローカルな変数を定義できるようになりました
// varは関数スコープなのでローカルな変数を使いたい場合は即時関数でがんばってた
var hoge = 'hello';
(function(){
var hoge = 'bye bye';
})();
console.log(hoge); // hello
// letはブロックスコープなのでだいじょうぶ
let hoge = 'hello';
{
let hoge = 'bye bye';
}
console.log(hoge); // hello
MDNからの引用ですがletの使用例でよく見かけるは、forで使う以下のパターン
var list = document.getElementById("list");
for (let i = 1; i <= 5; i++) {
let item = document.createElement("li");
item.appendChild(document.createTextNode("Item " + i));
item.onclick = function (ev) {
//iはletで宣言した変数なのでブロックごとに異なるiのインスタンスが渡される
console.log("Item " + i + " is clicked.");
};
list.appendChild(item);
}
変数iはletで定義されていてforのブロックのスコープなので意図通りに動作しますが、varで宣言した場合は関数のスコープになってしまい、item要素がクリックされたときにはiの最終値である6が返ってしまいます
(forEach使うし関係無いとか言わないで・・・)
Promise、Async/Await
これも抑えておかないと最近のJavaScriptを読むのは辛いやつです
解説する記事はたくさんあるのでかなり今更なんですが、最近はPromiseを返すメソッドも多いので知らないとしんどい。
非同期処理を順番に実行したいとき、どうしてもコールバック地獄になってしまうのを解決できる仕組みです。
(MDNからの引用です)
// コールバック地獄
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
// Promiseを使うとこう書ける
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
この例、パッと見だとわからないのでAjax通信(非同期処理)におけるXMLHttpRequestとFetchAPIの違いを見てみます。
//XHRは従来のコールバック型
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", function(responseText){
return JSON.parse(responseText); //同期処理なので大きいと処理がブロックされる
});
xhr.open("GET", "http://www.example.org/movies.json");
xhr.send();
//fetchはPromiseを返す
fetch('http://example.com/movies.json')
.then(function(response) { // レスポンスが帰ってきたらjsonにパースする
return response.json();
})
.then(function(myJson) { // jsonのパースが終わったらコンソールに出力
console.log(JSON.stringify(myJson));
});
うん、整理はされたけど言うほどか?
(XHRのJSON周りが同期処理なので思ったほどコールバック地獄にならなかった)
Promiseも結局then地獄な気もします。
そこでもう一歩踏み込んでAsync/Awaitにしてみます
// asyncを指定した関数内でawait構文が使用可能になる
async function fetchToJson(){
const response = await fetch('http://example.com/movies.json');
const myJson = await response.json();
console.log(JSON.stringify(myJson));
}
かなり見やすいですね!
await構文はPromiseを返す関数に対して指定すると、そのPromiseがresolvedになるまで待ってから値を取り出して返してくれます。
Promiseについて細かく説明すると長くなるので、詳しくは例によってMDNに任せます。
まとめ
サーバーサイドでたまにjQuery触ってたような人間だった自分がいきなりReactのコードを渡されて辛かったポイントと、最近同僚に「最近のJSわかんね」という感じのことを聞かれたのでまとめてみました。
アドベントカレンダー遅れてごめんなさい。