JavaScriptではファイルの記述にいろいろな方法がある。C++と比べるとカプセル化や名前空間が直接的な機能として提供されないが、パターンを使うことによってエラーが出にくいよう工夫することができる。JavaScriptの機能も多く出てくるが、1つのファイルの書き方としてどうすべきかに主眼を置いて解説する。
勿論、複数の書き方を組み合わせることも可能である。
比較のため普通の書き方をまず見てみよう。
const kLogForText= "console.log text: ";
var text = 0;
function Log()
{
console.log(kLogForText + );
}
Log();
text = "text変数に書き込みます。";
Log();
変数定義と定数定義、関数定義と逐次処理を行っている。変数定義と定数定義、関数定義には名前重複に気を付けなくてはならない。
JavaScriptの多重配列である。
var FieldData = [
[[
['r','r','r'],
['r','w','w'],
['r','w','w'],
],[
['r','r','r'],
['r','w','w'],
['w','w','p'],
]],[[
['r','r','r'],
['r','w','w'],
['r','w','w'],
],[
['r','r','r'],
['r','w','w'],
['r','w','w'],
]]
];
数値で参照できるが、添え字に定数が何度も必要ならプロパティを使うことを検討しよう。
プロパティをうまく使うことによってインターフェースのような書き方ができる。
var Fpi = {
String00: function (i)
{
if (i < 10)
{
return "0" + Number(i);
}
return Number(i);
},
CanMove: function (tipData)
{
if (tipData == "w")
{
return true;
}
return false;
}
}
let s = Fpi.String00(5); のような使い方になるので名前空間を使用しているような書き方になる。コードの階層関係や依存関係を示すのにも便利である。またユーティリティ関数に使ってもいいだろう。
C++のクラスに近い書き方も使いやすい。複数のインスタンス化を行うような場合には有効だろう。
class Mount {
constructor(width, height) {
this.width_ = width;
this.height_ = height;
this.m_ = Array(height);
for (let y = 0; y < height; y++)
{
this.m_[y] = Array(width);
}
}
Reset(mountData)
{
for (let y = 0; y < this.height_; y++)
{
for (let x = 0; x < this.width_; x++)
{
this.m_[y][x] = mountData[y][x];
}
}
}
Get(x, y) {
return this.m_[y][x];
}
Set(x, y, value)
{
this.m_[y][x] = value;
}
}
thisメンバにアクセスるときはthisを付けなければならなく、この辺は厳しいルールである。逆に言えば、名前衝突の心配がないので、名前空間の機能がないJavaScriptには適しているのだろう。変数は最後に"_"を付けることによってプライベート変数にするような規約にもできる。一方で、C++の構造体に近い使い方もできる。
クラス機能をいろいろ使ってみよう。変数の初期化とプライベートフィールド、プロトタイプメソッドである。
class TopCursor {
publicValue = 0;
#privateValue = 0;
get privateValue()
{
return this.#privateValue;
}
set privateValue(value) {
this.#privateValue = value;
}
}
#を付けるとプライベートフィールド宣言になり、クラス内からのみ参照できるカプセル化機能である。C++に近づいてきた。
変数のまとまりをコピーしながら拡張していくような使い方には継承もよいだろう。
statusclass.js
class StatusClass {
name;
hp;
constructor(name='', hp=0)
{
this.Init(name, hp);
}
Init(name, hp)
{
this.name = name;
this.hp = hp;
}
CopyFrom(srcStatusClass)
{
this.name = srcStatusClass.name;
this.hp = srcStatusClass.hp;
}
}
statusbareclass.js
class StatusBareClass extends StatusClass
{
unique; // StatusClass
level;
constructor(unique, level = 1)
{
super();
super.CopyFrom(unique);
this.unique = unique;
this.level = level;
}
}
基底クラスの変数やメソッドを呼び出すにはsuperを付ける必要があるので注意しよう。継承はコードの追跡が困難になりがちなので、本当に継承が必要かどうかは十分に検討したい。
クラスは#を使ったプライベートでカプセル化するが、他の書き方もある。
var ItemView = (function()
{
const cursor = Symbol('cursor');
class ItemView {
constructor() {
this[cursor] = -1;
}
get cursor()
{
return this[cursor];
}
set cursor(value)
{
this[cursor] = value;
}
}
return ItemView;
})();
var itemView = new ItemView();でインスタンス化する。外からcursorへはアクセスできない。Symbolを用いた書き方である。
以下のようなクラス機能を使った方が良いだろう。
class ItemViewClass {
#cursor;
constructor() {
this.#cursor = -1;
}
get cursor()
{
return this.#cursor;
}
set cursor(value)
{
this.#cursor = value;
}
}
リファクタリングする際の参考になるだろう。