class Hoge { method1() { ・・・ } method2 = () => { ・・・ } }
method1とmethod2の書き方の意味は違うのでしょうか?どのように使いわけるのでしょうか?
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
method1
は通常の書き方です。method2
は、2018年3月3日現在、仕様候補(stage 3)として提案中であるClass field declarations(クラスフィールド宣言)を用いた書き方です。**まだ、仕様には含まれておらず、また、このまま仕様に含まれることが確定されたわけではありません。**Babelでのトランスパイルは対応していますが、ブラウザでは対応している物が一つもありません。
さて、method2
ですが、これはインスタンスメソッドの定義と言うより、インスタンス変数の定義です。現在のECMAScriptではclassの内側にメソッド以外は直接書くことができません。そこで、Javaのような他の言語のように、インスタンス変数も宣言できるようにしたのがクラスフィールド宣言になります。このクラスフィールド宣言により、インスタンス作成時に、そのインスタンス変数がプロパティとして作成されます。また、宣言と共に初期化子(代入演算子=
と右辺のこと、これ自体はオプション)があれば、その値で初期化されるとなります。
つまり、文法的には
JavaScript
1class Hoge { 2 x = 0; 3 y = () => { return this.x; }; 4}
でのx
とy
はどちらもインスタンス変数であり、Hoge
のインスタンス作成時に、それぞれ0
と() => { return this.x; }
に初期化されます。ただ、ECMAScriptではインスタンス変数はプロパティです。値が関数であるプロパティはメソッドです。ですので、y
自体がメソッドであると言っても問題ありません。
ここで重要なのは、それぞれ代入される右辺が評価されるタイミングがそれぞれのインスタンス作成時であり、this
がそのインスタンスであると言うことです。つまり、このタイミングで評価される() => { return this.x; }
においてのthis
はそのインスタンス自身に束縛されることになります。
うん、(自分で書いたのに)なんかよくわからない。ということで、ここまで踏まえて、これがどのようなときに役に立つかをみながら、その動きを理解したいと思います。まず、次のようなコードをあったとします。
JavaScript
1class CountSec { 2 constructor(count) { 3 this.count = count; 4 } 5 countDown() { 6 console.log(this.count); 7 if (this.count > 0) { 8 this.count--; 9 setTimeout(this.countDown, 1000); 10 } 11 } 12} 13const ct = new CountSec(10); 14ct.countDown();
指定された回数分だけ1秒毎(あまり正確ではない)にカウントダウンするという意図のコードです。**このコードはこのままでは正常に動作しません。**二回目のcountDown
の呼び出しではthis
がそのインスタンスではなくなるからです。これはアロー関数やbind
等でthis
が束縛されていない関数がコールバック等で呼び出されるとき、this
は呼び出し側の関数に依存することに起因します。いわゆるthis
問題というものです。これを現在のECMAScript 2017で修正する方法は三つです。
一つ目は、アロー関数で囲むことです。アロー関数は評価時にthis
を束縛することができますので、コールバックでの呼び出し時もthis
が変わることはありません。
JavaScript
1class CountSec { 2 constructor(count) { 3 this.count = count; 4 } 5 countDown() { 6 console.log(this.count); 7 if (this.count > 0) { 8 this.count--; 9 setTimeout(() => { 10 this.countDown(); 11 }, 1000); 12 } 13 } 14} 15const ct = new CountSec(10); 16ct.countDown();
二つ目は、bind
を用いてthis
に束縛してしまうと言う方法です。こちらも、コールバックでの呼び出し時にthis
は変わりません。
JavaScript
1class CountSec { 2 constructor(count) { 3 this.count = count; 4 } 5 countDown() { 6 console.log(this.count); 7 if (this.count > 0) { 8 this.count--; 9 setTimeout(this.countDown.bind(this), 1000); 10 } 11 } 12} 13const ct = new CountSec(10); 14ct.countDown();
上二つはsetTimeout()
が呼び出される度に新たな関数を作成しているのと同じです。ですが、初めからthis
が束縛されているcountDown
が一つあれば十分です。そこで、三つ目がコンストラクタでbind
した関数をプロパティ(それは即ちそのインスタンス専用のメソッドです)として登録してしまうという方法です。
JavaScript
1class CountSec { 2 constructor(count) { 3 this.count = count; 4 this.countDown = this.countDown.bind(this); 5 } 6 countDown() { 7 console.log(this.count); 8 if (this.count > 0) { 9 this.count--; 10 setTimeout(this.countDown, 1000); 11 } 12 } 13} 14const ct = new CountSec(10); 15ct.countDown();
これらの方法は、いずれも初めのコードよりは冗長です。よく読めば意味がわからないわけではありませんが、なぜそんなことをするのかという意図がつかみにくいです。ましてや、一つ目と二つ目は毎回関数が生成されること、三つ目はbind
の位置とメソッド定義位置が離れてしまうことを考えると、あまり良い方法とは言えません。
そこで考えられたのがクラスフィールド宣言を使う方法です。これを使った場合は次のように書けます。
JavaScript
1class CountSec { 2 constructor(count) { 3 this.count = count; 4 } 5 countDown = () => { 6 console.log(this.count); 7 if (this.count > 0) { 8 this.count--; 9 setTimeout(this.countDown, 1000); 10 } 11 } 12} 13const ct = new CountSec(10); 14ct.countDown();
countDown
は親であるprototypeに登録されるようなメソッドではなく、インスタンス変数ではありますが、this
を束縛すると言うことはそのインスタンス専用になっている事であり、そこは問題にはなりません。this
の束縛があるという違い以外は通常のメソッドと同様に使えます。最初の三つのコードのような問題もありません。
さて、この方法を使う機会が思いつかないという人が居るかも知れませんが、使う機会が多いものを知っていますので、紹介します。それはReactです。Handling Events - Reactにその実例が載っています。Reactではクリックなどのイベント発生時に呼び出す関数を登録するのですが、そのままthis.handelEvent
等と書いてしまうと同じ問題が発生します。それを防ぐ手段として、クラスフィールド宣言は有効とされています。Reactを使う場合、いずれにしてもJSXのためにBabelをつかいますから、Babelが対応しているクラスフィールド宣言を使ってもさほど問題が無いと考えられます。
最後に、通常とのメソッドとの違いです。一番大きいのは、このクラスフィールド宣言ではインスタンスが生成させる度にそのインスタンス変数としての関数が生成されると言うことです。そのため、本当に必要であるという場合以外は使用すべきではないでしょう。
【おまけ】
CoffeeScriptならもっとすっきり書けるよ!
CoffeeScript
1class CountSec 2 constructor: (count) -> 3 this.count = count 4 countDown: => 5 console.log(this.count) 6 if this.count > 0 7 this.count-- 8 setTimeout(this.countDown, 1000) 9ct = new CountSec(10) 10ct.countDown()
投稿2018/03/03 11:23
編集2018/03/03 11:34総合スコア21737
0
いまのところ、正式に取り入れられているのはmethod1
の書き方のみです。
method2
で書くと、メソッドがプロトタイプに乗っからない形となりますので、特殊な使い方をしたい場合以外にはメリットはありません(Babel)。
投稿2018/03/03 05:55
編集2018/03/03 06:01総合スコア145876
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/03/03 13:23