最新JavaScript 「ECMAScript2015」を誰にもバレずに、こっそりさわってみる
最近癒しを求めて、こそこそJavaScriptを書いている
クリエイターのSAWAMICHIOです。
実はJavaScriptは2015年6月に「ECMAScript2015」「通称ES6?」と呼ばれる新仕様が発表され、
大きな進化を遂げでいるのを今更ながらピックアップしてみました。
JS以外のプログラミング言語を深く知るわけではないのでなんとも言えないのですが、
新仕様で特に注目を集めてそうなのがclass構文の追加です。
それまではプロトタイプという非常にわかりにくい苦行のような書き方だったのですが、
ES6ではよりポピュラーな記法でクラスを定義できるようになったようです。
↑よくある例です。はい。
関数を作ってそれをnew演算子を介して呼び出すと急にコンストラクタとして動作し、
インスタンス化したオブジェクトから親クラスのメソッドにアクセスできるというやつです。
↓同じことをこのように書き換えることができます。
「class クラス名」でクラスを定義する書き方は、
JavaやC言語ではおなじみらしいのですがワタシは知りません。
クラスからオブジェクトを生成するコンストラクタは、そのものズバリ
「constructor」で定義するようです。
コンストラクタの定義は省略することもでき、
省略した場合は、「constructor(){}」という空のコンストラクタが
暗黙のうちに定義されます。
クラスを定義したら、new演算子でクラスからオブジェクトを生成できます。
この辺は一緒ですね。
またクラスの定義は下記のようにクラスリテラルで記述して、
変数に代入する書き方もできます。
上記までのものは、単に書き方が変わっただけっぽかったのですが、
クラス構文には静的メソッドを持たせることができます。
この場合、オブジェクトをわざわざインスタンス化することなく、
「クラス名.静的メソッド」で実行できます。
ここではsetter/getterを使って
Personクラスにageプロパティを追加しています。
クラスの重要な機能の一つにオブジェクトどうしの継承関係を築けるというのがあります。
JS自体、Objectを頂点とした壮大な宇宙を構成しているように
あるクラス(親クラス)から、そのクラスのメソッドやプロパティを引き継いだ(子クラス)を定義できる機能です。
従来はプロトタイプ継承というJS特有の?やり方で実現していましたが、新しい構文では「extends」という構文で継承を実現できるようになり
よりシンプルにわかりやすく表現することができます。
新しいclass構文によってガラリと変わった印象を持ちますが、内部的にはやはり従来通りのプロトタイプベースのようです。
その証拠にclass構文で定義した Personをtypeof演算子で確認すると function(関数)と表示されます。
さらに「Person.prototype.プロパティ = 関数リテラル」という従来の記法でメソッドを追加することも可能です。
表面的にはガラリと変わって新しい言語かのようになりましたが、実態は
今までのJavaScriptそのものということで安心できますね。
いちいち学習し直さないでいいですから。
他にも気になる機能がたくさん追加された「ECMAScript2015」ですが、それはまた次の機会に
御免。
従来のプロトタイプベースでのクラス(コンストラクタ)の定義
function Person( name, gender ) { this._name = name this._gender = gender } Person.prototype.string = function() { return this._name + 'は' + this._gender + 'です。' } var sawa = new Person( 'SAWAMICHIO', '男' ) console.log( sawa.string() )
最新のクラス構文でのコンストラクタの定義
class Person { constructor( name, gender ) { this._name = name this._gender = gender } string() { return this._name + 'は' + this._gender + 'です。' } } const sawa = new Person( 'SAWAMICHIO', '男' ) console.log( sawa.string() )
クラスリテラルを使ってコンストラクタを定義する
const Person = class { constructor( name, gender ) { this._name = name this._gender = gender } string() { return this._name + 'は' + this._gender + 'です。' } }
static修飾子で静的メソッドを定義できる
class Calc { static sum( num1, num2 ) { return num1 + num2 } } console.log( Calc.sum( 5, 5 ) )
[実行結果]
10
setter/getterを使ったプロパティの定義
class Person { set age( value ) { console.log( 'setter呼び出し' ) if ( value < 0 ) { throw new RangeError( 'ageは正数で' ) } this._age = value } get age() { console.log( 'getter呼び出し' ) return this._age } } const kawaguchi = new Person() kawaguchi.age = 22 console.log( kawaguchi.age ) kawaguchi.age = -22 console.log( kawaguchi.age )
[実行結果]
setter呼び出し
getter呼び出し
22
setter呼び出し
x RangeError: ageは正数で
setのコードブロックがsetterで、ageプロパティに値を設定した時に自動的に実行されます。
同じくgetのコードブロックがgetterで、ageプロパティから値を取得しようとした時に自動的に実行されます。
setterを省略して読み取り専用のプロパティを定義する
class Person { get age() { console.log( 'getter呼び出し' ) return this._age = 22 } } const kawaguchi = new Person() console.log( kawaguchi.age ) kawaguchi.age = 92 console.log( kawaguchi.age )
getterを省略して書き込み専用のプロパティを定義する
class Person { set age( value ) { console.log( 'setter呼び出し' ) if ( value < 0 ) { throw new RangeError( 'ageは正数で' ) } this._age } } var kawaguchi = new Person() kawaguchi.age = 22 console.log( kawaguchi.age )
extends構文を使ったクラスの継承
class Person { constructor( name, gender ) { this._name = name this._gender = gender } string() { return this._name + 'は' + this._gender + 'です。' } } class FrontierVision extends Person { constructor( name, gender, job ) { super( name, gender ) this._job = job } string() { return this._name + 'は' + this._job + 'です。' } } const kawaguchi = new FrontierVision( '川口', '男', 'エンジニア' ) console.log( kawaguchi.string() )