ES2015がリリースされてから、すでに約2年半が経過しようとしています。
皆さんはES2015の書き方には慣れてきたでしょうか?
今更感はありますが、今回はES2015以前のJSをES2015を使ってリファクタリングしてみようと思います。
ES2016/ES2017については、面倒だったので今回は割愛させて頂きます。
リファクタリング対象
今回リファクタリングしていく対象のソースコードです。
// Personクラスを定義
var Person = function(lastName, firstName) {
this.lastName = lastName || '山田';
this.firstName = firstName || '太郎';
}
// hello()メソッドを定義
Person.prototype.hello = function() {
return '私の名前は'+this.firstName+'です。';
}
// 引数で受け取った人物を紹介する関数
var introduce = function(persons) {
var length = persons.length;
for(var i = 0; i < length; i++) {
var person = persons[i];
var fullName = person.lastName + person.firstName;
console.log('あの人は'+fullName+'です。');
}
console.log('最後の紹介した人は'+fullName+'だよ。');
}
// Personのインスタンスを生成
var personA = new Person('小早川', 'セナ');
var personB = new Person('進', '清十郎');
// 私の名前はセナです。
console.log(personA.hello());
// あの人は小早川セナです。
// あの人は進清十郎です。
// 最後に紹介した人は進清十郎だよ
introduce([personA, personB])
class構文
プロトタイプの記述をclass構文を使って変更していきます。
散々、苦しめられたprototype
の記述が無くなり早速モダンな感じになりました。
※ class構文は、あくまでプロトタイプベースの糖衣構文です。
var Person = function(lastName, firstName) {
this.lastName = lastName || '山田';
this.firstName = firstName || '太郎';
}
Person.prototype.hello = function() {
return '私の名前は'+this.firstName+'です。';
}
class Person {
constructor(lastName, firstName) {
this.lastName = lastName || '山田';
this.firstName = firstName || '太郎';
}
hello() {
return '私の名前は'+this.firstName+'です。';
}
}
デフォルト引数
デフォルト引数を利用することで、undefined
のチェックが不要になりスッキリと書けるようになりました。
class Person {
constructor(firstName, lastName) {
this.lastName = lastName || '山田';
this.firstName = firstName || '太郎';
}
...
}
class Person {
constructor(firstName = '山田', lastName = '太郎') {
this.lastName = lastName;
this.firstName = firstName;
}
...
}
テンプレート文字列
``
(バッククォート)で文字列を囲むことで変数展開が出来ます。
展開する変数は${}
で囲みます。
文字列結合の記述が簡素になり非常に見やすくなりました。
hello() {
return '私の名前は'+this.firstName+'です。';
}
hello() {
return `私の名前は${this.firstName}です。`
}
アロー関数
アロー関数を使うことで、function式
よりも短い構文で記述が可能です。
引数が一つの時に限り()
は省略できます。
var introduce = function(persons) {
...
}
// 引数が一つなので
// var introduce = persons => {}
// と記述もできる
var introduce = (persons) => {
...
}
アロー関数はthis
を束縛しないので、jQueryなどを使う場合は注意してください。
// 誤った書き方
$('button').on('click', () => {
// [window]
// thisは束縛されないので、windowオブジェクトを指す
console.log($(this));
});
// 正しい書き方
$('button').on('click', function() {
// [button]
// thisは束縛されるので、jQueryオブジェクトを指す
console.log($(this));
});
let, constによる変数宣言
ES2015では新たな変数宣言のキーワードとしてlet
,const
が導入されました。
let
はブロックスコープによる変数宣言が可能になっており、if文の{}
の中だけで有効な変数を宣言できます。
const
は再代入が不可能な変数(定数)を宣言できます。
var introduce = (persons) => {
var length = persons.length;
for(var i = 0; i < length; i++) {
var person = persons[i];
var fullName = person.lastName + person.firstName;
console.log(`あの人は${fullName}です。`);
}
// fullName は最後のループの文字列になる
// var は関数スコープのため fullName は参照可能
console.log(`最後の紹介した人は${fullName}だよ。`);
}
const introduce = (persons) => {
const length = persons.length;
for(let i = 0; i < length; i++) {
let person = persons[i];
let fullName = person.lastName + person.firstName;
console.log(`あの人は${fullName}です。`);
}
// fullName はundefinedになる
// let はブロックスコープのため fullName は参照出来ない
console.log(`最後の紹介した人は${fullName}だよ。`);
}
リファクタリング結果
結果として見ると、そこまで大きく変わってないように見えませんか?
ES2015で書くと言っても、書き方の構造を大きく変更する訳では無いので、全く新しいものと抵抗を感じる必要は無いと思います。
class Person {
constructor(firstName = '山田', lastName = '太郎') {
this.lastName = lastName;
this.firstName = firstName;
}
hello() {
return `私の名前は${this.firstName}です。`
}
}
const introduce = (persons) => {
const length = persons.length;
for(let i = 0; i < length; i++) {
let person = persons[i];
let fullName = person.lastName + person.firstName;
console.log(`あの人は${fullName}です。`);
}
console.log(`最後の紹介した人は${fullName}だよ。`);
}
const personA = new Person('小早川', 'セナ');
const personB = new Person('進', '清十郎');
// 私の名前はセナです。
console.log(personA.hello());
// あの人は小早川セナです。
// あの人は進清十郎です。
// "ReferenceError: fullName is not defined
introduce([personA, personB])
さいごに
今回、紹介出来なかった機能として分割代入やPromiseなど、ES2015には他にも便利な機能が沢山あります。
また、ES2016/ES2017にもArray.prototype.includes()やAsyncFunctionなどの便利な機能があるので、慣れてきたら使ってみてください。