ServerSide ES6 (๑╹◡╹๑)ノ timakin / @timadayon
ServerSide ES6 (๑╹◡╹๑)ノ
timakin / @timadayon
こんにちはこんにちは!
Profile: - DeNA入社1年目 - エンジニア - github: timakin - blog: timakin.log
およそ8~9ヶ月ほど リブセンス(様) にはお世話になりました
Twitter: @timadayon
idも画像も違う。 気分で下手に 変えていいもんじゃない。
[某所での初タスク]
JavaScriptでのちょっとしたDOM操作
僕が取った手段
getElementById(..) onClick=“hoge()”
jQueryってものがあってな。
リブセンス様、その節はほんとうに 温かい目で見守ってくださって ありがとうございました。
ほんっとに死ぬほど感謝してます。
時は流れましたが…
今日は
一部ES6の紹介と、 フロントエンドではなく、 サーバーサイドの言語として
ES6を実際に使ってみたときの話。
• Block scoping
• Classes (strict mode only)
• Collections
• Typed arrays
• Generators
• Binary and Octal literals
• Object literal extensions
• Promises
• New String methods
• Symbols
• Template strings
• Arrow Functions
ES6 features ship with Node.js by default(v4.2.1)
• Block scoping
• Classes (strict mode only)
• Collections
• Typed arrays
• Generators
• Binary and Octal literals
• Object literal extensions
• Promises
• New String methods
• Symbols
• Template strings
• Arrow Functions
ES6 features ship with Node.js by default(v4.2.1)
実際手元で触ってて 気になったのは これくらい…
• Block scoping • 変数のスコープ、定数化 • letで宣言された変数は、その変数が定義されたブロックにスコープが限定される。
• constで定数化。
function varTest() { var x = 31; if (true) { var x = 71; // same variable! console.log(x); // 71 } console.log(x); // 71}
function letTest() { let x = 31; if (true) { let x = 71; // different variable console.log(x); // 71 } console.log(x); // 31}
const MY_FAV = 7;
// this will fail silently in Firefox and Chrome (but does not fail in Safari)MY_FAV = 20;
// will print 7console.log("my favorite number is: " + MY_FAV);
// trying to redeclare a constant throws an error const MY_FAV = 20;
// the name MY_FAV is reserved for constant above, so this will also failvar MY_FAV = 20;
• Promises • 非同期処理でメソッドを繋げる時、コールバック地獄を回避しつつ、メソッドチェーンで書くことのできる子。
• もともとES6どうこうの前に結構使われてた。
function timeout(duration = 0) { return new Promise((resolve, reject) => { setTimeout(resolve, duration); })}
var p = timeout(1000).then(() => { return timeout(2000);}).then(() => { throw new Error("hmm");}).catch(err => { return Promise.all([timeout(100), timeout(200)]);})
• Classes (strict mode only) • 目玉商品。JavaScriptで(他の言語と同じような)クラスがかけるよ。
• 最高だよ。もうprototypeとか書かなくて済むよ。 • 書かなくて済むけど、prototypeの糖衣構文です。
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); }}
class Dog extends Animal { speak() { console.log(this.name + ' barks.'); }}
• Arrow Functions • 関数型っぽいfunctionの書き方、レキシカルなthis(thisの参照先が、意図に反してグローバルオブジェクトになって、「あれ?このインスタンスのプロパティ更新されないぞ?」「値が入ってないぞ?」というようなことにならない。そのメソッドが呼び出されるオブジェクトの中に閉じている状態)を簡単に扱える。
• というかthisが自動で関数にbindされる
function Person(){ this.age = 0;
setInterval(() => { this.age++; // |this| properly refers to the person object }, 1000);}
var p = new Person();
• Generators (Function) • イテレーターを簡単に作れる特殊な関数。任意のタイミングで処理を止められる。オブジェクト自身が今どこまで処理を進めたか覚えておき、next()メソッドを使って止めたところから先の処理を再度実行する。
• 特に用事がなかったので僕は使いませんでした。
function* anotherGenerator(i) { yield i + 1; yield i + 2; yield i + 3;}
function* generator(i){ yield i; yield* anotherGenerator(i); yield i + 10;}
var gen = generator(10);
console.log(gen.next().value); // 10console.log(gen.next().value); // 11console.log(gen.next().value); // 12console.log(gen.next().value); // 13console.log(gen.next().value); // 20
実際に使ってみた例
モバイルの勉強がてら、 SwiftでNewsアプリ(笑)を作ってたんですが、 SwiftTaskというSwift版Promise実装
みたいなやつが、マルチリクエスト用途で使いにくくて、 「いっそNodeでAPI置いて、 モバイルが叩くエンドポイントは
1つに統一しよう…どうせ分化する予定だったし」 と思った。
その際に、ちょうどNode v4.0.0の io.jsとNode.js統合があったので、
じゃあもうES6に置き換えようと決めた。 (途中まではES5でした)
具体例
コントローラー部分
"use strict";
const _ = require('underscore');const express = require('express');const router = express.Router();const newsCollectionModel = require('../models/newsCollection.model');
router.get('/', (req, res) => { let newsCollection = new newsCollectionModel();
newsCollection.get(req, (items) => { res.json({ response: items }); });});
モデル部分
class News { constructor(props) { this.title = props.title; this.url = props.url; this.origin = props.origin; this.image_uri = props.image_uri; this.created_at = props.created_at; }
...}
_convertNewsModelToObject(items) { return _.flatten(items).map((item) => item.toObject()); }
get(req, cb) { let promises = _.map(urls[req.query.country],
(url) => { return fetch(url, (res) => { return res.body.items }) });
Promise.all(promises) .then((items) => _.flatten(items) ) .then((items) => this._filterFeedlyItemsByImageExistance(items) ) .then((items) => this._createNewsModelCollectionByFeedly(items) ) .then((items) => this._convertNewsModelToObject(items) ) .then((items) => this._sortByCreatedAt(items) ) .then((items) => cb(items), (err) => console.error(err.stack) ); }
• Arrow Functionによってコールバック関数に使う文字数を減らせて、視覚的に健康。コントローラーだと多用するので結構変わりますよ。
• 特にモデルクラスだと思いますが、constructorとそれ以外のメソッドを同じclass内の記述としてかけるので、包含関係を意識しやすいです。(prototypeを使って別々に書かなきゃいけない頃とは違い、ちゃんと一個のクラスとして書けてる感じがします)
• underscore.jsとArrow functionの合わせ技で、軽いビジネスロジックなら1行で済ませることができました。(underscore初見の人からすると邪魔っぽいので、ここは要検討)
• まとめて複数のAPIをコールするときとか、request用のpromiseインスタンスを作るメソッドを汎用の関数として用意しておく。
• Promise.allで一気に結果を取得した後、then(…).then(…)で、綺麗に値をフィルター/加工できます。
• requireした結果を格納してる変数を誤って更新しないように、全部constで持っておくと安全。
• package.jsonにnodeのバージョンを記載しておけば、herokuで最新版が動かせるので、すぐ試せます。webpackとかいらない。
以下要検討の課題
• APIから返ってきた値のフィルタリングの処理を、条件ごとに別関数に分けた。
• が、関数の数を減らそうと思うとthenの中身が地獄になり、関数の数を増やせばthenの中身は1行で済んでも、関数が増えすぎて地獄になるか、underscoreで読みにくくなるか、という板挟みだった。苦肉の策としてメソッド名を長くして中身の処理を命名から推測しやすくした。
• 短く書けるからって調子乗ったら死にました。(これはES6ではなく完全に僕の問題です)
• modulesがない…実装はよ… • とはいえrequire50倍速という噂もあるし、別にもうmodule.exportsで事足りる。
• Strong ModeかStrict Modeか迷って、中途半端にStrict Mode使ってましたが、Strong Modeで移行すればよかった。。
ともあれ、サーバーサイドの 言語として非常に使い勝手がいいです。 expressも軽量だから 書き換えがしやすいです。
フロントエンドでES6使うことが 多いですが、ES6のシンタックスだけ 速習したいなら、サーバーサイドから 入ったほうがいいと思います。 Node最新バージョン入れたらすぐ 動くのと、フロントエンド フレームワークの激流に気を取られず、 シンタックスの学習に 集中できるからです。
おわり。