今回は、ECMAScript 2015(ES2015)で新たに導入された構文を紹介します。
Let + Const
let、constは新たに追加された変数宣言で、var宣言では不可能だった再代入の禁止や再宣言の禁止をすることができます。また、スコープがブレース{}の中に閉じるようになったので、今までよりも保守性の高いプログラムが作成しやすくなりました。
let 「再宣言」が不可
リスト1:letの使用例
6 | // Uncaught TypeError: Identifier 'role' has already been declared |
const 「再宣言」も「再代入」も不可
リスト2:constの仕様例
01 | // constを宣言する時は、初期値を入れなければいけません。 |
03 | // Uncaught SyntaxError: Missing initializer in const declaration |
05 | const role = 'Developer' |
08 | const role = 'Reporter' |
09 | // Uncaught TypeError: Identifier 'role' has already been declared |
13 | // Uncaught TypeError: Assignment to constant variable. |
letとconstの使い分けですが、まずconstを使用し再代入が必要な場合のみletを使うようにしましょう。こうすることで、別の開発者がソースを読むときに変数の後の動きが予測しやすくなるのでリーダブルなコードになります。GitHubのStar 44,000以上と絶大な人気を誇るAirbnbのJavaScriptコーディング規約においても、「まずconstを使い、varは避けましょう」ということが書かれています。
Airbnb JavaScript Style Guide
アロー関数(Arrows)
アロー関数式は、従来のfunction式を短く簡潔に書くことができるものです。ただし、この式は単なる省略記法というだけでなく、thisの値を束縛することに注意が必要です。function式ではthisは自身を参照していましたが、アロー関数式でオブジェクトのメソッドとして呼び出された場合は、コンテキストのオブジェクトがthisとなります。
ブロック文体(block body)
リスト3:アロー関数の例
1 | let sum = (param1, param2) => { |
引数が1つの場合、丸括弧は省略できます。
引数を取らない場合、丸括弧が必要です。
即時関数は下記のように記述します。
リスト6:即時関数の記述例
2 | return navigator.userAgent |
簡潔文体(concise body)
波括弧を省略した場合、暗黙的に「return文」になります。
リスト7:簡潔文体
1 | let squared = x => x * x |
下記のようなobjectリテラルを返すブロック文体のメソッドを、簡潔文体で記述したい場合には、「({ foo: 1 })」のように丸括弧で囲ってください。そうしないと、「{}」が文として解析されてしまうためです。
リスト9:簡潔文体での記述例
2 | let fn = () => { foo: 1 } |
6 | let fn = () => ({ foo: 1 }) |
Note
jQueryを使用してイベント登録をする際にアロー関数を使う場合は、下記のように「(this)」に代えて、「(e.currentTarget)」を使用することで、DOMの取得が可能になります。
リスト10:jQueryのイベント登録にアロー関数を使う
1 | $(target).on('click', (e) => { |
2 | $(e.currentTarget).removeClass('hoge') |
アロー関数を用いると、Arrayメソッドを簡潔に記述することができます。
リスト11:アロー関数を使ったArrayメソッドの書き方
24 | users.map(user => user.age) |
27 | users.filter(user => user.gender === 'female') |
41 | users.every(user => user.age >= 18) |
44 | users.some(user => user.gender === 'male') |
Classes
これまでのJavaScriptではclassを作る仕組みがなく、prototypeでclass likeな実装をするしかありませんでした。しかしES2015でようやく、正式にclassが標準のインターフェースとして実装されました。
class構文を使用することで、複雑な記述になりがちなprototype構文を使用することなく、他のプログラミング言語のように簡潔にクラスを記載することができるようになります。
ES5までのclassの書き方とES2015でのclassの書き方の違いは、下記のとおりです。
ES5でのclass likeな書き方
リスト12:ES5でのclass likeな書き方
01 | var Person = function(name, age) { |
11 | sayHello: function() { |
12 | console.log("Hello I'm " + this.getName()) |
ES2015でのclassの書き方
リスト13:ES2015でのclassの書き方
02 | constructor(name, age) { |
12 | console.log("Hello I'm " + this.getName()) |
メソッド定義
Constructor method
constructorメソッドは、クラスによって定義されるオブジェクトが生成されるときに実行されるメソッドです。主にクラス内で共通して使われるプロパティの初期値を定義する際などに使用されます。constructorというメソッドは1つのクラスに1つしか定義できず、2つ以上定義されている場合はSyntax Errorとなります。
リスト14:constructorメソッド
02 | constructor(name, age) { |
08 | let alice = new Person('Alice', 7) |
ES2015のクラスの仕様では、クラス直下の本体部分で定義できるのはメソッドのみです。クラス変数としてデータを保持する変数や定数は定義できないため、注意が必要です。クラス直下でインスタンスを定義しようとした場合は、Syntax Errorとなります。
リスト15:クラス直下に変数は定義できない
2 | this.name = 'Alice' // Uncaught SyntaxError: Unexpected token |
クラスに変数を持たせる場合は、コンストラクタ内で記述する必要があります。
リスト16:変数はコンストラクタ内に記述する
3 | this.name = 'Alice' // OK |
コンストラクタ内で定義した変数は、Getter/Setterを使用して取得・設定が可能です。
リスト17:コンストラクタ内で定義した変数のハンドリング
03 | this.length = arr.length |
04 | this.width = arr.width |
09 | return this.length * this.width |
14 | this.length = arr.length |
15 | this.width = arr.width |
19 | const calculator = new Calculator({length: 10, width: 5}) |
20 | console.log(calculator.area) // 50 |
22 | calculator.parameter = {length: 20, width: 10} |
23 | console.log(calculator.area) // 200 |
Prototype methods
クラスのインスタンスから呼び出せるメソッドを、prototypeメソッドといいます。
リスト18:prototypeメソッド
02 | constructor(height, width) { |
08 | return this.height * this.width |
12 | const square = new Square(10, 10) |
Static methods
staticキーワードを使用することで、クラスに静的(static)メソッドを定義できます。静的メソッドは、クラスのインスタンスを生成することなく、「クラス名.メソッド名」の形で呼び出すことができます。
リスト19:静的メソッド
03 | return Object.keys(obj).map(key => obj[key]) |
07 | return Object.keys(obj).map(key => [key, obj[key]]) |
11 | ObjectPolyfill.values({ |
16 | // ["Alice", 19, "female"] |
18 | ObjectPolyfill.entries({ |
23 | // [["name", "Alice"], ["age", 19], ["gender", "female"]] |
Sub classing with extends
クラスを継承する際は、extendsを使用します。クラス宣言内にextendsを記述することで、他のクラスを継承することができます。
リスト20:extendsを用いた継承
02 | constructor(name, age) { |
12 | console.log("Hello I'm " + this.getName()) |
16 | class Employee extends Person { |
17 | constructor(name, age, salary) { |
27 | let employee = new Employee('Alice', 19, 10000) |
28 | employee.getName() // Alice |
29 | employee.sayHello() // Hello I'm Alice |
30 | employee.getSalary() // 10000 |
Super class calls with super
親クラスのメソッドを呼び出すには、superを使用します。
リスト21:superを用いて親クラスのメソッドを呼び出す
06 | console.log(this.name + ' makes a noise.') |
10 | class Lion extends Cat { |
13 | console.log(this.name + ' roars.') |
17 | let lion = new Lion('Mike') |
デコレーターパターン
ES2015の次期バージョンでは、動的にオブジェクトの機能を拡張できるデコレーターパターンの導入が検討されています。現在はStage2にて検討が進められています。デコレーターパターンが導入されれば、クラスやメソッドに対してアノテーションを使用することで動的にクラスの機能を追加していくことができるようになり、さらにclass構文を活用する場が広がります。
なおアノテーションの関数に引数を渡すには、functionを返す必要があります。
リスト22:デコレーターパターン導入後のコード例
03 | this.firstname = "Alice" |
04 | this.lastname = "Liddell" |
09 | return this.firstname + " " + this.lastname |
13 | function readonly(value) { |
14 | return function (target, key, descriptor) { |
15 | descriptor.writable = !value |
現時点での検討状況、提案内容については下記のサイトを参照してください。
ECMAScript Active Proposals
Decorators summary
Enhanced Object Literals
ES2015では、オブジェクトリテラルに便利な構文が追加されました。
Shorthand
オブジェクトのキー名と変数名が同じだった場合、省略して記述することができます。
リスト23:Shorthandの例
3 | let options = { user_id, count } |
Methods
オブジェクトのメソッドが、function句を使わずに定義できるようになりました。
リスト24:function句なしでメソッドを定義
Computed property
オブジェクトのキー名を計算して評価することが可能になりました。
リスト25:Computed propertyの例
01 | let searchResultItems = [ |
03 | title: "株式会社リクルートテクノロジーズ", |
07 | title: "株式会社リクルートホールディングス", |
12 | let insertItems = searchResultItems.map((item, index) => { |
14 | ["title_" + "index"]: item.title, |
15 | ["url_" + "index"]: item.url |
21 | // "title_0": "株式会社リクルートテクノロジーズ", |
25 | // "title_1": "株式会社リクルートホールディングス", |