15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

HameeAdvent Calendar 2017

Day 13

古き良きJSをES2015でリファクタリング

Last updated at Posted at 2017-12-12

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などの便利な機能があるので、慣れてきたら使ってみてください。

15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?