【実践OOP】 JScriptでグローバルな名前空間からプロトタイプチェーンでメソッド参照する。ついでに外部ファイル化も実施する。

はじめに

 記事が増えてきてオブジェクト指向プログラミング(OOP)化に着手してもいいのかなと思い始めて来たので、基本知識紹介記事の充実も進めつつ 本題となるオブジェクト指向プログラミング的な記事も書き進めていきたいと思います。

 今回は「はじめの一歩」となる内容ではありますが、ここまで書いたらこのマガジン終了していいんじゃね? くらいの内容を盛り込んでお届けします。長くて読み飽きてしまうかもしれませんが、お付き合い願います。

 又、今後追加していくライブラリ用途や応用編的記事の外殻はこの記事の内容を基本にしていますので、なにかあったらここへ戻って頂ければと思います。


復習:プロトタイプオブジェクト

 JavaScript では 関数定義と同時に空のprototypeオブジェクトが用意されます。唯一プロトタイプが存在しないのがプロトタイプ自身だそうです。

 検証の為プロトタイプを定義していない関数のプロトタイプの型を調べてみます。

 下記のスクリプトを拡張子"js" のテキストファイルにコピペして実行してみます。

/* テストスクリプト01 */
var fFunc = function(){};  //空の関数

WScript.Echo(
	"【未定義のプロトタイプオブジェクトが存在することの確認】","\n"+ 
	"["+ typeof(fFunc)           +"]" , "fFunc"          ,"\n"+ 
	"["+ typeof(fFunc.prototype) +"]" , "fFunc.prototype"
);

 実行結果は以下の通りでした。関数定義するだけでプロトタイプオブジェクトがオブジェクト型として存在していることが確認できました。

E:\test> cscript test01.js

【未定義のプロトタイプオブジェクトが存在することの確認】
[function] fFunc
[object] fFunc.prototype


 ちょっと気になる面白い実験をしてみます。今度はただのオブジェクトにプロトタイプオブジェクトがあるのか確認します。

/* テストスクリプト02 */
var objA  = {};            //空のオブジェクト
var OBJB  = OBJB || {};    //グローバルな名前空間
OBJB.prototype = {};       //prototype というサブ空間
OBJB.prototype.objB  = {}; //空のオブジェクト

WScript.Echo(
	"["+ typeof(objA)            +"]" , "objA"           ,"\n"+ 
	"["+ typeof(objA.prototype)  +"]" , "objA.prototype" ,"\n\n"+ 
	"["+ typeof(OBJB)            +"]" , "OBJB"           ,"\n"+ 
	"["+ typeof(OBJB.prototype)  +"]" , "OBJB.prototype" ,"\n\n"
);
※ グローバルな名前空間の定義で使用している”||” はor演算子です。これはグローバルオブジェクトの名前衝突回避をするコーディング例です。グローバルオブジェクトが存在していればそのまま使い、無ければ新規に定義を行います。


 上記「テストスクリプト02」を実行すると以下の様な結果となりました。

E:\test> cscript test02.js

[object] objA
[undefined] objA.prototype

[object] OBJB
[object] OBJB.prototype

 まず”objA” 側から見ていきます。"objA.prototype" は「undefined」と表示されています。これは未定義という意味なので、ただのオブジェクトでは自身の”prototype”は(自動的に)作られないということですね。

 今度は オブジェクト "OBJB" 側を見てみましょう。こちらはどうなっているのでしょうか? よく見ると普通のオブジェクトに付く”prototype” の正体が判ります。

 一見すると "OBJB.prototype" というプロトタイプオブジェクトが存在しているように見えますが、"OBJB.prototype = {}; " の行と WScript.Echo() から "OBJB" 関連の記述をコメントアウト(削除)して実行すると エラーになってしまいます。以下エラーメッセージです。

E:\test\test02.js(5, 1) Microsoft JScript 実行時エラー: 'OBJB.prototype'Null またはオブジェクトではありません。

 名前空間は階層構造でないといけないので、第二階層すっ飛ばして第三階層の定義はできません。 見ての通り”OBJB.prototype.objB” は定義できませんでした。ということは、"OBJB.prototype" はプロトタイプオブジェクトではなく、サブ名前空間だということになります。

 調べてみたら "prototype" って予約語になっていませんでした。ぶったまげました。

 "~.prototype" という名前の名前空間が作れてしまうということは、継承されない理由がプロトタイプオブジェクトを参照しているつもりで実は名前空間を見ていた、という不具合にはまる可能性があるということです。

 なんか気楽に書き始めたら結構重要な内容になってしまった気がします。以上蛇足?

 

グローバルな名前空間からのプロトタイプチェーン

 プロトタイプオブジェクトと名前空間の復習はできたので、プロトタイプチェーンを混ぜてみましょう。

 (1) シンプルにプロトタイプチェーンをやってみる

 サンプルコードは名前空間を使って関数を三つ定義したのち、継承しながら最後に for…inループでインスタンス内のメンバを列挙させることで継承の状態を確認しています。

 Object.create()の代替関数は 「【WSH事始め】継承を整理/Object.create() 同等の関数と コンストラクタ連鎖のcall(),appry()」でやったので説明割愛します。尚、Object.create() の代替関数自身を継承するスマートな方法が思い浮かばなかったので、関数のままで使用しています。
 サンプル考えるのに疲れてしまって見やすさ優先で作っていますが、本来プロパティはオブジェクト固有のもののはずなので、プロトタイプにはメソッドのみ書くのがきれいなコーディングであるのだと思います。実際にはプロトタイププロパティも活用していますが。

 コードを見て 誤解を招かないように再度伝えておきますが、プロトタイプオブジェクトは関数定義時に自動的に作成されるので、継承時に参照される関数をインスタンス化する必要はありません。(「テストスクリプト06」 ではインスタンス生成を”myappC” だけにした実行例を掲示しています。)

/* テストスクリプト03 */
var MYAPP = MYAPP || {};                //グローバルな名前空間オブジェクト
MYAPP.objectcreate = function(proto) {  // Object.create()の代替関数
	function ctor() { };
	ctor.prototype = proto;
	return new ctor();
};


MYAPP.AppA = function(){};  // 関数
MYAPP.AppA.prototype = {propA:"abcd"};  

MYAPP.AppB = function(){};  // 関数
MYAPP.AppB.prototype = MYAPP.objectcreate( MYAPP.AppA.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */

MYAPP.AppC = function(){};  // 関数
MYAPP.AppC.prototype = MYAPP.objectcreate( MYAPP.AppB.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */
MYAPP.AppC.prototype.propC = "lmn"
MYAPP.AppC.prototype.debug_member = function(objconstructor,iinstance){
	/* ---------------------------------------- */
	/* インスタンスに含まれるメンバを一覧にする */
	/* ---------------------------------------- */
	var strMsg = "";
	for (var key in objconstructor){
		strMsg += key + "  " 
	}
	WScript.Echo(
		       "▽",iinstance,"のメンバ ▽"
		,"\n"+ strMsg
		,"\n"+ "△",iinstance,"のメンバ △" + "\n"
	);
};


var myAppA = new MYAPP.AppA();  // コンストラクタ MYAPP.AppA をインスタンス生成
var myAppB = new MYAPP.AppB();  // コンストラクタ MYAPP.AppB をインスタンス生成
var myAppC = new MYAPP.AppC();  // コンストラクタ MYAPP.AppC をインスタンス生成
myAppC.debug_member(myAppA,"myAppA");  // インスタンスのメンバ表示
myAppC.debug_member(myAppB,"myAppB");  // インスタンスのメンバ表示
myAppC.debug_member(myAppC,"myAppC");  // インスタンスのメンバ表示

WScript.Echo(
	"myAppC.propA=", myAppC.propA
);

 これを実行すると以下の通り。"MYAPP.AppA" に定義したプロパティ "propA" が "MYAPP.AppC" → "MYAPP.AppB" → "MYAPP.AppA" とたどって参照できたことが確認できると思います。

E:\test> cscript test.js

▽ myAppA のメンバ ▽
propA
△ myAppA のメンバ △

▽ myAppB のメンバ ▽
propA
△ myAppB のメンバ △

▽ myAppC のメンバ ▽
propC  propA  debug_member
△ myAppC のメンバ △

myAppC.propA= abcd
 この様に目的のメソッドを探して一個一個のプロトタイプを順繰りに参照していく行為がプロトタイプチェーンというわけです。でも使用したいメソッドを含むプロトタイプオブジェクトが複数存在している場合には、継承元を一度に列挙した方が楽そうなのですが、そのようなコーディングテクニックを紹介しているところを見たことがありません。考えるに、“プロトタイプリスト” や”プロトタイプ階層” ではないので、例えば二分木検索の様に枝葉の末端まで来たら分岐地点まで戻って別の枝をたどる様な動きはしないのかもしれません。基本的には末端に着いたらそこで終わり、では困るので次の枝には直接飛び移る。他にも枝があれば更に飛び移り続ける事で必要なプロトタイプオブジェクトを参照し続けるのが”プロトタイプチェーン” だと考えます。最も私が知らないだけの可能性もありますのでもっとスマートなやり方があればそれに越した事はありません。


 (2) パラメータを追加して継承の状態を確認する

 次にパラメータを増やしてやってみます。定義した関数は全て空っぽだったので、プロパティや変数を追加します。実行するとプロトタイプオブジェクトのメンバのみ継承されることが確認できると思います。

/* テストスクリプト04 */
var MYAPP = MYAPP || {};                //グローバルな名前空間オブジェクト
MYAPP.objectcreate = function(proto) {  // Object.create()の代替関数
	function ctor() { };
	ctor.prototype = proto;
	return new ctor();
};


MYAPP.AppA = function(){    // 関数
	this.propAZ = "関数Aのプロパティ";
}; 
MYAPP.AppA.prototype = {propA:"abcd"};  

MYAPP.AppB = function(){    // 関数
	this.propBZ = "関数Bのプロパティ";
}; 
MYAPP.AppB.hensuB = "名前空間の変数B";     // 名前空間"MYAPP.AppB" に変数"hensuB" を定義
MYAPP.AppB.prototype = MYAPP.objectcreate( MYAPP.AppA.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */

MYAPP.AppC = function(){    // 関数
	this.propCC = "関数Cのプロパティ";
}; 
MYAPP.AppC.prototype = MYAPP.objectcreate( MYAPP.AppB.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */
MYAPP.AppC.prototype.propC = "lmn"
MYAPP.AppC.prototype.debug_member = function(objconstructor,iinstance){
	/* ---------------------------------------- */
	/* インスタンスに含まれるメンバを一覧にする */
	/* ---------------------------------------- */
	var strMsg = "";
	for (var key in objconstructor){
		strMsg += key + "  " 
	}
	WScript.Echo(
		       "▽ インスタンス(",iinstance,")のメンバ ▽"
		,"\n"+ strMsg
		,"\n"+ "△ インスタンス(",iinstance,")のメンバ △" + "\n"
	);
};


var myAppA = new MYAPP.AppA();  // コンストラクタ MYAPP.AppA をインスタンス生成
var myAppB = new MYAPP.AppB();  // コンストラクタ MYAPP.AppB をインスタンス生成
var myAppC = new MYAPP.AppC();  // コンストラクタ MYAPP.AppC をインスタンス生成
myAppC.debug_member(myAppA,"myAppA");  // インスタンスのメンバ表示
myAppC.debug_member(myAppB,"myAppB");  // インスタンスのメンバ表示
myAppC.debug_member(myAppC,"myAppC");  // インスタンスのメンバ表示

WScript.Echo(
	"myAppC.propA=", myAppC.propA
	,"\n"+"myAppB.hensuB=", myAppB.hensuB
	,"\n"+"MYAPP.AppB.hensuB=", MYAPP.AppB.hensuB
);

 実行結果です。

E:\test> cscript test04.js

▽ インスタンス( myAppA )のメンバ ▽
propA  propAZ
△ インスタンス( myAppA )のメンバ △

▽ インスタンス( myAppB )のメンバ ▽
propA  propBZ
△ インスタンス( myAppB )のメンバ △

▽ インスタンス( myAppC )のメンバ ▽
propC  propA  propCC  debug_member
△ インスタンス( myAppC )のメンバ △

myAppC.propA= abcd
myAppB.hensuB=
MYAPP.AppB.hensuB= 名前空間の変数B

解説1:各関数内で定義した"this.prop??" は各インスタンス内ではメンバとなっていますが、継承されない為、インスタンス"myappC" では自身のコンストラクタ内に定義した "propCC" のみ存在します。

解説2:関数"MYAPP.AppB" の定義後追加した "MYAPP.AppB.hensuB" は一見プロパティの様に見えますが、コンストラクタでもプロトタイプでもない為、インスタンス"myappB"のメンバとしては存在しません(エラーにならない模様)。その代わりに何故か名前空間化している"MYAPP.AppB" の単なる変数としてはアクセス可能となっています。


 さて、ここまでやるとそれなりに罠の仕込みが出来上がっています。

 一応、成功例だけでは完全理解に届かないと思うので、失敗例も数例挙げておきます。実際、応用しようと思った途端に不具合にハマって丸一日解析してた、というような事は開発畑の人間には日常茶飯事ですので、不具合に落ち入りやすいコーディングパターンは備忘録としては外せません。


 (3) コンストラクタと同じ名称の名前空間を作ってしまってプロパティのつもりがただの変数になっている例

 「復習:プロトタイプオブジェクト」の節の最後で懸念していた内容です。

 前節「(2) パラメータを追加して継承の状態を確認する」の"MYAPP.AppB.hensuB" が実例となるようなケースです。

 因みに、通常オブジェクトに”OBJB.prototype” という名前のサブ空間を作った、前出のテストスクリプト02に手を加えて"OBJB" をインスタンス化する処理を追加すると、new した時点で「このオブジェクトではサポートされていない操作です。」と言われて怒られます。まぁ関数でないので当然ですが。


 (4) 名前衝突が起きて継承が失敗するパターン:プロトタイプをオブジェクト・リテラルで定義する時は注意する事

 「【前提知識】プロトタイプベースのオブジェクト指向の基礎知識」で紹介されている通り、プロトタイプ定義はオブジェクト・リテラル(連想配列的に)定義が可能です。ところが、継承した後にオブジェクト・リテラルを使ってプロトタイプの定義を追加すると継承内容が上書きされます。

 サンプルコードでは "MYAPP.AppB.prototype" と ”MYAPP.AppC.prototype” で継承する処理をした後、オブジェクト・リテラルの形式で定義追加をしています。

/* テストスクリプト05 */
var MYAPP = MYAPP || {};                //グローバルな名前空間オブジェクト
MYAPP.objectcreate = function(proto) {  // Object.create()の代替関数
	function ctor() { };
	ctor.prototype = proto;
	return new ctor();
};


MYAPP.AppA = function(){    // 関数
	this.propAZ = "関数Aのプロパティ";
}; 
MYAPP.AppA.prototype = {propA:"abcd"};  

MYAPP.AppB = function(){    // 関数
	this.propBZ = "関数Bのプロパティ";
}; 
MYAPP.AppB.hensuB = "名前空間の変数B";                             // 名前空間"MYAPP.AppB" に変数"hensuB" を定義
MYAPP.AppB.prototype = MYAPP.objectcreate( MYAPP.AppA.prototype ); /* WSH.objectcreate : Object.create()の代替関数 */
MYAPP.AppB.prototype = {propB1:"ijk",propB2:"CCCC"};               // オブジェクト・リテラル でメンバ追加

MYAPP.AppC = function(){    // 関数
	this.propCC = "関数Cのプロパティ";
}; 
MYAPP.AppC.prototype = MYAPP.objectcreate( MYAPP.AppB.prototype ); /* WSH.objectcreate : Object.create()の代替関数 */
MYAPP.AppC.prototype = {                                           // オブジェクト・リテラル でメンバ追加
	 c:"lmn"
	,debug_member : function(objconstructor,iinstance){
	/* ---------------------------------------- */
	/* インスタンスに含まれるメンバを一覧にする */
	/* ---------------------------------------- */
	var strMsg = "";
	for (var key in objconstructor){
		strMsg += key + "  " 
	}
	WScript.Echo(
		       "▽ インスタンス(",iinstance,")のメンバ ▽"
		,"\n"+ strMsg
		,"\n"+ "△ インスタンス(",iinstance,")のメンバ △" + "\n"
	);
	}
};


var myAppA = new MYAPP.AppA();  // コンストラクタ MYAPP.AppA をインスタンス生成
var myAppB = new MYAPP.AppB();  // コンストラクタ MYAPP.AppB をインスタンス生成
var myAppC = new MYAPP.AppC();  // コンストラクタ MYAPP.AppC をインスタンス生成
myAppC.debug_member(myAppA,"myAppA");  // インスタンスのメンバ表示
myAppC.debug_member(myAppB,"myAppB");  // インスタンスのメンバ表示
myAppC.debug_member(myAppC,"myAppC");  // インスタンスのメンバ表示

WScript.Echo(
	"myAppC.propA=", myAppC.propA
);

実行結果です。

E:\test> cscript test05.js

▽ インスタンス( myAppA )のメンバ ▽
propA  propAZ
△ インスタンス( myAppA )のメンバ △

▽ インスタンス( myAppB )のメンバ ▽
propB2  propB1  propBZ
△ インスタンス( myAppB )のメンバ △

▽ インスタンス( myAppC )のメンバ ▽
c  propCC  debug_member
△ インスタンス( myAppC )のメンバ △

myAppC.propA=

解説1:"MYAPP.AppB.prototype" という名前で追加のプロパティ定義を行っているので継承内容が上書きされています。ですので、インスタンス"myappB" のメンバから "MYAPP.AppA"のプロパティ"propA" が消えています。

解説2:"MYAPP.AppC.prototype" も同様に名前衝突により継承内容が上書きされています。当然インスタンス"myappC" からプロパティ ”propA”にアクセスできません。

対策1:「増長コーディングを避けるため、オブジェクトリテラルでコーディングすると見やすくなる」と外部リンク先では書かれていますが、ことプロトタイプチェーンとなる場合は 継承後の追加プロパティ、追加メソッドは "~.prototype.プロパティ名 又は メソッド名" で統一した方が良いです。因みに個人的には 今どの関数のプロトタイプを作っているのかわかり易いのでこちらの書き方の方が好みです。

対策2:最終的にインスタンスになる関数(ここでは"MYAPP.AppC")のプロパティ、メソッドはプロトタイプ定義でなく、関数定義内に記述すれば上書きされません。(サンプルコードでは更にチェーンが続く場合を想定している為、このような記述を取っています。)



外部ファイルをincludeする形に書き換えてみる

 名前空間使ってプロトタイプチェーンでメソッド参照しているスクリプトをファイル分割してみましょう。とその前に、ちょっと細工を施します。

小細工1:分割理由としてconstプロパティ、Commonメソッドを用意する。
小細工2:グローバルな名前空間を複数使う。
小細工3:プロトタイプ定義にオブジェクト・リテラルを使わない。

 名前空間関係のサイトを見ると「グローバルな名前空間は一つにする」のが良いとする所が多く、非推奨なのか、単純化しているだけなのかハッキリしません。なのでここでは単純化しているだけという前提で話を進めて行きます。

 なぜグローバルな名前空間を複数用意したかというと別ファイル化した時、グローバルな名前空間の名称を統一する必要はなく、ファイル毎又は、用途別に作成しても問題無い事を言いたかったからです。


 スクリプトの説明:プロトタイプで定義したメンバがインスタンス化した"MYAPP.AppC" で "WSH.constproperty" → "WSH.commonmethod" → "MYAPP.AppA" → "MYAPP.AppB" → "MYAPP.AppC" と引き継がれてきていることを確認します。  
/* テストスクリプト06 */
/* ---------------------------------------------- */
/* 空のコンストラクでグローバルオブジェクトを作る */
/* ---------------------------------------------- */
var WSH = WSH || function(){};
/* ------------------------------------------ */
/* その配下にいろいろ定義(ただの変数と関数) */
/* ------------------------------------------ */
WSH.objFS/*未使用*/ = WScript.CreateObject("Scripting.FileSystemObject");
WSH.objWshShell/*未使用*/ = WScript.CreateObject("WScript.Shell");
WSH.objectcreate = function(proto) {   // Object.create()の代替関数
	function ctor() { };
	ctor.prototype = proto;
	return new ctor();
};

/* -------------------- */
/* WSH.CONST プロパティ */
/* -------------------- */
WSH.constproperty = function(){};     //  サブネームスペース作成
WSH.constproperty.prototype.objFS = WScript.CreateObject("Scripting.FileSystemObject");
WSH.constproperty.prototype.objWshShell = WScript.CreateObject("WScript.Shell");
WSH.constproperty.prototype.fncFS/*未使用*/       = function(){ return WScript.CreateObject("Scripting.FileSystemObject"); };
WSH.constproperty.prototype.fncWshShell/*未使用*/ = function() { return WScript.CreateObject("WScript.Shell"); };

/* ------------------- */
/* WSH.COMMON メソッド */
/* ------------------- */
WSH.commonmethod = function(){};     //  サブネームスペース作成
WSH.commonmethod.prototype = WSH.objectcreate( WSH.constproperty.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */
WSH.commonmethod.prototype.debug_member = function(objconstructor,iinstance){
	/* ---------------------------------------- */
	/* インスタンスに含まれるメンバを一覧にする */
	/* ---------------------------------------- */
	var strMsg = "";
	for (var key in objconstructor){
		strMsg += key + "  " 
	}
	WScript.Echo(
		       "▽ インスタンス(",iinstance,")のメンバ ▽"
		,"\n"+ strMsg
		,"\n"+ "△ インスタンス(",iinstance,")のメンバ △" + "\n"
	);
};


var MYAPP = MYAPP || {};                //グローバルな名前空間オブジェクト

MYAPP.AppA = function(){};  // 関数
MYAPP.AppA.prototype = WSH.objectcreate( WSH.commonmethod.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */
MYAPP.AppA.prototype.propA = "abcd";  

MYAPP.AppB = function(){};  // 関数
MYAPP.AppB.prototype = WSH.objectcreate( MYAPP.AppA.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */

MYAPP.AppC = function(){};  // 関数
MYAPP.AppC.prototype = WSH.objectcreate( MYAPP.AppB.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */
MYAPP.AppC.prototype.propC = "lmn"


var myAppC = new MYAPP.AppC();  // コンストラクタ MYAPP.AppC をインスタンス生成
myAppC.debug_member(myAppC,"myAppC");  // インスタンスのメンバ表示

WScript.Echo(
	"myAppC.propA=", myAppC.propA
);

 実行結果です。

E:\test> cscript test06.js

▽ インスタンス( myAppC )のメンバ ▽
propC  propA  fncWshShell  fncFS  objWshShell  objFS  debug_member
△ インスタンス( myAppC )のメンバ △

myAppC.propA= abcd


 では上記「テストスクリプト06」をグローバルな名前空間部分と処理実行部分で3分割します。wsfファイル化の他、 eval() を使用した例も紹介しておきます。

 "WSH_COMMON.js" と"MyApp.js" はそれぞれの名前空間部分をコピペしただけです。"MyAppC.wsf" はお決まりのタグで囲った中にインスタンス生成と実行部分を張り付けています。尚、3ファイル共に同じフォルダ内にいるなら<script>タグ内で パス指定は不要です。
/* テストスクリプト06-1 (WSH_COMMON.js)*/
/* ---------------------------------------------- */
/* 空のコンストラクでグローバルオブジェクトを作る */
/* ---------------------------------------------- */
var WSH = WSH || function(){};
/* ------------------------------------------ */
/* その配下にいろいろ定義(ただの変数と関数) */
/* ------------------------------------------ */
WSH.objFS/*未使用*/ = WScript.CreateObject("Scripting.FileSystemObject");
WSH.objWshShell/*未使用*/ = WScript.CreateObject("WScript.Shell");
WSH.objectcreate = function(proto) {   // Object.create()の代替関数
	function ctor() { };
	ctor.prototype = proto;
	return new ctor();
};

/* -------------------- */
/* WSH.CONST プロパティ */
/* -------------------- */
WSH.constproperty = function(){};     //  サブネームスペース作成
WSH.constproperty.prototype.objFS = WScript.CreateObject("Scripting.FileSystemObject");
WSH.constproperty.prototype.objWshShell = WScript.CreateObject("WScript.Shell");
WSH.constproperty.prototype.fncFS/*未使用*/       = function(){ return WScript.CreateObject("Scripting.FileSystemObject"); };
WSH.constproperty.prototype.fncWshShell/*未使用*/ = function() { return WScript.CreateObject("WScript.Shell"); };

/* ------------------- */
/* WSH.COMMON メソッド */
/* ------------------- */
WSH.commonmethod = function(){};     //  サブネームスペース作成
WSH.commonmethod.prototype = WSH.objectcreate( WSH.constproperty.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */
WSH.commonmethod.prototype.debug_member = function(objconstructor,iinstance){
	/* ---------------------------------------- */
	/* インスタンスに含まれるメンバを一覧にする */
	/* ---------------------------------------- */
	var strMsg = "";
	for (var key in objconstructor){
		strMsg += key + "  " 
	}
	WScript.Echo(
		       "▽ インスタンス(",iinstance,")のメンバ ▽"
		,"\n"+ strMsg
		,"\n"+ "△ インスタンス(",iinstance,")のメンバ △" + "\n"
	);
};
/* テストスクリプト06-2 (MyApp.js) */
var MYAPP = MYAPP || {};                //グローバルな名前空間オブジェクト

MYAPP.AppA = function(){};  // 関数
MYAPP.AppA.prototype = WSH.objectcreate( WSH.commonmethod.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */
MYAPP.AppA.prototype.propA = "abcd";  

MYAPP.AppB = function(){};  // 関数
MYAPP.AppB.prototype = WSH.objectcreate( MYAPP.AppA.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */

MYAPP.AppC = function(){};  // 関数
MYAPP.AppC.prototype = WSH.objectcreate( MYAPP.AppB.prototype );	/* WSH.objectcreate : Object.create()の代替関数 */
MYAPP.AppC.prototype.propC = "lmn"
/* テストスクリプト06-3 (MYAppC.wsf)*/
<job id="IncludeExample">
 <script language="JScript" src="WSH_COMMON.js"/>
 <script language="JScript" src="MyApp.js"/>
 <script language="JScript">

	var myAppC = new MYAPP.AppC();  // コンストラクタ MYAPP.AppC をインスタンス生成
	myAppC.debug_member(myAppC,"myAppC");  // インスタンスのメンバ表示

	WScript.Echo(
		"myAppC.propA=", myAppC.propA
	);

 </script>
</job>


 eval() を使った場合。実行ファイルのみ載せます。(紹介しているサイトが見つからないので昔作った古いソースから引っ張ってきました。今見るとセキュリティ云々よりReadFileの処理を挟まないといけないところがエレガントさに欠けますね。)

/* テストスクリプト06-4 (MYAppC_eval.js)*/
var objFS = WScript.CreateObject("Scripting.FileSystemObject");
eval( objFS.OpenTextFile("C:/tmp/WSH_COMMON.js", 1).ReadAll() );
eval( objFS.OpenTextFile("C:/tmp/MyApp.js", 1).ReadAll() );

var myAppC = new MYAPP.AppC();  // コンストラクタ MYAPP.AppC をインスタンス生成
myAppC.debug_member(myAppC,"myAppC");  // インスタンスのメンバ表示

WScript.Echo(
	"myAppC.propA=", myAppC.propA
);
 尚、ファイル分割時に文法エラーが発生した場合、「実行したファイルのn行目のmカラム目で〜」という文言が出ますが、実際にエラーが発生したファイル名が表示されません。実行ファイル含めた分割ファイルのどれかでエラー発生しているので、それさえ突き止めればファイル名以外は正しく表示されているのでメッセージ内容のままでエラー修正が行えます。(昔はこの事に気づかず、一々ファイル連結させてバグ取りしていました。泣)


CONSTプロパティとCOMMONメソッドは溜め込んでいくと開発が楽になる

 最後になりますが、今後予定しているの応用編とかでは、外部ファイルをincludeする例は サンプルソースが物理的に増える分手間がかかるので 今のところ紹介しない方針でいます。

 尚、わたし個人の使い方としては CONSTプロパティにWSH関連のオブジェクトを入れて、COMMONメソッドには基本操作となる処理を定義して使っています。関数は結構ストックされていくのでCONSTとCOMMONは更にファイル分割すると見直しに便利だったりします。


 最後までご覧頂きありがとうございました。


この記事が気に入ったらサポートをしてみませんか?