static変数を定義しようとするとエラーが出る

明太子マン

ユーザー
自作プラグインファイル内で、次のようにクラス式を使ってクラスを定義しました。
JavaScript:
Scene_Hoge.prototype.HogeClass = class {
        constructor(){
            // 略
        }

        static hoge = 'hoge';
        
        // 略
};
これを実行したところ、static変数を定義している行で次のようなエラーが出ました。
コード:
Uncaught SyntaxError: Unexpected token =

クラス宣言でも試してみましたが、結果は同じでした。

その後、念の為にコアスクリプトを最新版に更新してみましたが、何も変わりませんでした。

この記事によると、既にstatic構文はツクールMVで問題なく使えるらしいのですが、なぜこのようなエラーが出るのか、検討がつきません。

どなたか教えて頂けないでしょうか。
 

fspace

ユーザー
本題とは関係ないところですが、プロトタイプに対してクラスを定義しているところに少し怪しさを感じます。JavaScriptではJavaやC#のように内部クラスを定義する意味はあまりないので、外部に定義して参照する形を検討してみては?


ちなみにDarkPlasmaさんの回答に少し補足すると、今のところJavaScript(ECMAScript)の仕様にクラスフィールドの構文はありません。

クラスフィールドの構文はJavaScriptの言語仕様を策定しているTC39に提案(proposal)として挙げられている状態で、現在Stage 3(Stage 4で仕様化)です。

GitHub - tc39/proposal-class-fields: Orthogonally-informed combination of public and private fields proposals
GitHub - tc39/proposal-static-class-features: The static parts of new class features, in a separate proposal

各ブラウザはだいたい実装していますが、Safariのみまだstaticフィールドに対応していないみたいです。

Can I use... Support tables for HTML5, CSS3, etc
 

明太子マン

ユーザー
パブリッククラスフィールド - MDN

staticメソッドは使えるようになっていますが、staticフィールドはChromium 72からサポートされる機能です。
MV 1.6.x のデフォルトである NWjs 0.29.4 は、 Chromium 65 ですので、まだ使えないようですね。
回答ありがとうございます。
staticフィールドを使わない方法を考えてみようと思います。
 

明太子マン

ユーザー
本題とは関係ないところですが、プロトタイプに対してクラスを定義しているところに少し怪しさを感じます。JavaScriptではJavaやC#のように内部クラスを定義する意味はあまりないので、外部に定義して参照する形を検討してみては?


ちなみにDarkPlasmaさんの回答に少し補足すると、今のところJavaScript(ECMAScript)の仕様にクラスフィールドの構文はありません。

クラスフィールドの構文はJavaScriptの言語仕様を策定しているTC39に提案(proposal)として挙げられている状態で、現在Stage 3(Stage 4で仕様化)です。

GitHub - tc39/proposal-class-fields: Orthogonally-informed combination of public and private fields proposals
GitHub - tc39/proposal-static-class-features: The static parts of new class features, in a separate proposal

各ブラウザはだいたい実装していますが、Safariのみまだstaticフィールドに対応していないみたいです。

Can I use... Support tables for HTML5, CSS3, etc
回答ありがとうございます。
「クラスを外部に定義して参照したほうがよい」とのことですが、具体的な理由が知りたいです。
私はスコープを狭めようという短絡的な考えであのコードを書いたのですが、限定的な用途のクラスでも外部から参照するようにしたほうがいいですか?
それとも、あのコードでは「スコープを狭める」という目的はそもそも達成されませんか?
 

明太子マン

ユーザー
このQ&Aを見つけた方のために、情報共有します。

あの後、staticフィールドの代替案を調べたところ、次のようなページを見つけました。
ES6 class variable alternatives - Stack Overflow

この中のある回答から引用します。
For a true class variable you'd want to do something like the following:

class MyClass {}
MyClass.foo = 'bar';

From within a class method that variable can be accessed as this.constructor.foo (or MyClass.foo).

These class properties would not usually be accessible from to the class instance. i.e. MyClass.foo gives 'bar' but new MyClass().foo is undefined

要するに、以下のように書くことで、現在の仕様でもstaticフィールドを実現できるらしいです。
JavaScript:
class MyClass {}
MyClass.foo = 'bar';

この書き方の場合も、staticフィールドと同じようにインスタンス経由では参照できず、
JavaScript:
this.constructor.foo
または
JavaScript:
MyClass.foo
と参照することになるとのことです。
 

fspace

ユーザー
「クラスを外部に定義して参照したほうがよい」とのことですが、具体的な理由が知りたいです。
私はスコープを狭めようという短絡的な考えであのコードを書いたのですが、限定的な用途のクラスでも外部から参照するようにしたほうがいいですか?
それとも、あのコードでは「スコープを狭める」という目的はそもそも達成されませんか?
クラスを参照できる範囲を限定したいということであれば、ブロックで囲むだけで十分です。
JavaScript:
{
    class Foo {}
}
const foo = new Foo(); // ReferenceError
プロトタイプに定義してしまうと、かえって簡単にアクセスできてしまいます。
JavaScript:
class Bar {}
Bar.prototype.Baz = class {};

const baz1 = new Bar.prototype.Baz(); // OK
const baz2 = new (new Bar()).Baz();   // OK

静的型付けのオブジェクト指向型言語の感覚からすると、クラスとオブジェクトはまったく別の次元のものだと思いますが、JavaScriptにおいて、class構文で定義されるのは単なる関数で、関数はオブジェクトの一種です。new演算子にしても、任意の関数に対して適用できます。
JavaScript:
class A {
    constructor(value) {
        this.x = value;
    }
}

function B(value) {
    this.x = value;
}

const a = new A(42);  // OK
const b = new B(42);  // OK

console.log(a.x);  // 42
console.log(b.x);  // 42
console.log(a instanceof A);  // true
console.log(b instanceof B);  // true
MyClass.foo = 'bar';で静的フィールドが表現できるのも、MyClassがオブジェクトに過ぎないからです。
 

げれげれ

ユーザー
明太子マンさん、こんにちばんわ~

既に手練れのお二人が回答されているので無用の口出しかもしれませんが、
ちょうどクラス定義やprototypeの仕組みについて、年末年始に記事としてまとめたものがあるので
もしかすると理解の手助けになるかもと思いしゃしゃり出てみます。
(@fspace さん>その節は添削ありがとうございました。)

ツクールの「おまじない」からの卒業(prototype編) #prototypeチェーンについて

class構文ではなくprototype構文、staticメソッドでなくインスタンスメソッドの視点から
記述した記事ですが、これらは一つの事象の裏表なので参考になるかもしれません。

JavaScriptにおけるクラスの正体がファンクションオブジェクトに過ぎないという点や、
prototypeとは結局どういうものでなぜインスタンスから参照できるのか、
裏を返せばどうすればインスタンスから参照できないstaticなメソッドを作れるのか、
という辺りについてまとめています。

もしお時間がお有りでしたらどうぞ~。
 
トップ