バッチ 変数展開
すぐに忘れるのでメモ。
修飾子
説明
%~1
%1 を展開し、囲んでいる二重引用符
("") がある場合は削除します。
%~f1
%1 を完全修飾パス名に展開します。
%~d1
%1 をドライブ文字に展開します。
%~p1
%1 をパスに展開します。
%~n1
%1 をファイル名に展開します。
%~x1
%1 をファイル拡張子に展開します。
%~s1
展開されたパスにショート
ネームだけを含めます。
%~a1
%1 をファイル属性に展開します。
%~t1
%1
をファイルの日付と時刻に展開します。
%~z1
%1 をファイル サイズに展開します。
%~$PATH:1
環境変数 PATH
に指定されているディレクトリを検索し、%1
を、最初に見つかったファイルの完全修飾名に展開します。
環境変数の名前が定義されていない場合、またはファイルが見つからない場合、この変数
修飾子
説明
%~dp1
%1
をドライブ文字とパスに展開します。
%~nx1
%1
をファイル名と拡張子に展開します。
%~dp$PATH:1
環境変数 PATH
に設定されているディレクトリから %1
を検索し、最初に見つかったディレクトリのドライブ文字とパスに展開します。
%~ftza1
%1 を、dir
の出力行のように展開します。
面倒くさいJavascriptの整理−prototype.js
id:higepon:20050831
id:higepon:20050901
でprototype.jsを知った。
すごい。
非常に便利だ。万歳!
動作確認がてら、昔懐かしい「ドラッグ可能なレイヤー」を実装してみた。
外部ファイル部分
var CDragable = Class.create(); CDragable.prototype = { initialize : function( elm ){ this.elm = $( elm ); this.elm.style.position = "absolute"; this.offsetX = 0; this.offsetY = 0; this.elm.onmousedown = this._onMouseDown.bindAsEventListener(this); this.elm.onselectstart = this._onSelectStart.bindAsEventListener(this); } , target:null, _onSelectStart : function( event ) { //IE return false; }, _onMouseMove : function( event ){ var target = CDragable.prototype.target; if( target ) { target.elm.style.left = ( Event.pointerX( event ) - target.offsetX ).toString() + "px"; target.elm.style.top = ( Event.pointerY( event ) - target.offsetY ).toString() + "px"; } }, _onMouseDown : function ( event ){ if( !CDragable.prototype.target){ CDragable.prototype.target = this; var x = this.elm.style.left || this.elm.offsetLeft; var y = this.elm.style.top || this.elm.offsetTop; this.offsetX = Event.pointerX( event ) - parseInt( x ); this.offsetY = Event.pointerY( event ) - parseInt( y ); } }, _onMouseUp : function ( event ){ if( CDragable.prototype.target ){ CDragable.prototype.target = null; } } }; Event.observe( document , 'mousemove' , CDragable.prototype._onMouseMove , false ); Event.observe( document , 'mouseup' , CDragable.prototype._onMouseUp , false );
実装部
var mo; var po; window.onload = function() { //通常なら特に変数に格納する必要はないが一応 mo = new CDragable( "foo" ); po = new CDragable( "bar" ); }
タグ部
<div id="foo"> ドラッグ可能なレイヤー </div> <div id="bar"> ドラッグ可能なレイヤー2 </div>
base.js,event.jsを読みながら、30分程でこれができた。驚異的だ。
(zIndexの処理は面倒なので未実装)
prototypeが普通に動くか心配だったけど、どうやらこれで大丈夫らしい。
特にイベントの処理がすばらしいと思う。
bindAsEventListenerは直感的だし、Event.observeは最高だ。
document.onmousemoveを占有しないので、
Event.observe( document , 'mousemove' , CDragable.prototype._onMouseMove , false );
Event.observe( document , 'mouseup' , CDragable.prototype._onMouseUp , false );
この2行を迷いなくライブラリに追加しておける。
次は継承で遊んでみようか。
面倒くさいJavascriptの整理−var , this , prototype編
サンプルで動作確認
1.thisの動作
function ClassA( name ) { this.name = name; ClassA.prototype.getName = function() { return this.name; } } function ClassB( name ) { this.name = name; ClassB.prototype.getName = ClassA.prototype.getName; } var classA = new ClassA( "classA" ); var classB = new ClassB( "classB" ); alert( classA.getName() ); alert( classB.getName() );結果:ClassA:classA,ClassB:classB
new演算子はこのように振舞うようにするためのもの。
thisは実行時のオブジェクトを指すように振舞う。
そして、イベントハンドラでは普通にインスタンスメソッドを渡すとthisはelementを指すのでホゲる。
だから、イベントハンドラに素直にthisを書くとelementを指してしまうので何とかする必要があることが分かると思う。
また、例ではnewの動作を分かりやすくするためにClassBのprototypeにClassAのprototypeを代入しているが、この場合、ClassBがClassAに依存する形になっているので、不自然だが無視。
2.varの動作
function ClassA( name ) { var Name = name; ClassA.prototype.getName = function() { return Name; } } function ClassB( name ) { var Name = name; ClassB.prototype.getName = ClassA.prototype.getName; } var classA = new ClassA( "classA" ); var classB = new ClassB( "classB" ); alert( classA.getName() ); alert( classB.getName() );結果:ClassA:classA,ClassB:classA
varとthisの動作の違いを理解するべし。
特にvarは関数が宣言された位置が参照スコープの基準になる点が重要。
追記しておくと、参照スコープはfunction区切り。
3.仮引数と内部関数の動作
function ClassA( name ) { ClassA.prototype.getName = function() { return name; } } function ClassB( name ) { ClassB.prototype.getName = ClassA.prototype.getName; } var classA = new ClassA( "classA" ); var classB = new ClassB( "classB" ); alert( classA.getName() ); alert( classB.getName() );結果:ClassA:classA,ClassB:classA
ついでに内部変数varではなくて仮引数の場合も確認しておく。
仮引数についても、関数の内部に宣言された場合は同じ動作をする。
なぜか、prototypeに無名関数を代入する方法ではなく、以下のような直接宣言する方法は
function ClassA.prototype.getName()
IEでは問題ないがFFでは構文エラーになった。
普通はprototypeの宣言はコンストラクタ関数の外部に出すのであまり良いサンプルではないのでが、次のサンプルはprototypeの理解にも役立つように思える。
4.仮引数と内部関数とprototypeの動作
function ClassA( name ) { ClassA.prototype.getName = function() { return name; } } function ClassB( name ) { ClassB.prototype.getName = ClassA.prototype.getName; } var classA = new ClassA( "classA" ); var classB = new ClassB( "classB" ); var classAA= new ClassA( "classAA" ); alert( classA.getName() ); alert( classAA.getName() ); alert( classB.getName() );結果:classA:classAA,classAA:classAA,classB:classA
まず、classAとclassAAの結果に注目。
理解しやすいが、ClassAはインスタンスを作成する度にprototype.getName()を書き換える点を理解する。
結果、サンプルでclassA.getName()が呼び出されている部分でのgetNameの実態は、classAAインスタンスを作成したときのgetName関数ということになる。クラスClassAのインスタンスclassAもclassAAも同じprototypeを共有しているためこのようなことが起こる。これがprototypeの振る舞い。一方で、このようにコンストラクタ関数の中にprototypeを宣言する危険も示したつもり。次にclassBの結果にも注目。
ちょっと1つのサンプルに欲張りすぎた。ClassBのコンストラクタ関数で仮引数nameはもちろん参照されない。
また一見すると、ClassBのprototype.getNameはClassAのprototype.getNameを参照しているように思われるがそうではなく、この場合classBのgetNameはclassAインスタンス作成時のgetNameの無名関数を参照している。
だからその後、classAAのgetNameでClassAのprototype.getNameが上書きされても影響を受けない。
その上、classAインスタンス作成時に渡された引数"classA"もClassB.prototype.getNameから参照されているため解放されていない。クロージャを作成するときにはこの性質が利用される。
5.まとめ
function ClassA( name ) { var cnt=0; ClassA.prototype.getName = function() { return name += (cnt++); } } function ClassB( name ) { var cnt=0; this.getName = function(){return name += (cnt++); }; } var classA = new ClassA( "classA" ); var classAA = new ClassA( "classAA" ); alert( classA.getName() ); alert( classAA.getName() ); var classB = new ClassB( "classB" ); var classBB = new ClassB( "classBB" ); alert( classB.getName() ); alert( classBB.getName() );結果:classA:classAA0,classAA:classAA01,classB:classB0,classBB,classBB0
これを見て思い出せ自分!
この動作確認だけを考えるとprototypeなんか使わない方が懸命だと思ってしまうが、
継承を考えると。。。そうとも言えない気がする。
また、大量にインスタンスを作るときもコンストラクタ関数がでかい分パフォーマンスに影響すること必然。
面倒くさいJavascriptの整理−クラス編
Javascriptでクラスの書き方は色々あるのでまとめ
2.メンバ変数の宣言
メンバ変数はthisをつけて宣言する。
thisの意味さえ分かっていれば問題ない。function SuperClass(name){ this.name = name; }当たり前だけど、メンバ変数はコンストラクタでしか宣言できない(多分)。
アクセス修飾子はないから、全部public。遮蔽もやろうと思えばやれないことはないだろうけど、特に意味がなさそう。
3.メンバ関数
メンバ関数の書き方がいろいろあってよく分からない。
調べてみたら、大体これくらいあった。
1.コンストラクタ内で内部関数として宣言する。function SuperClass(name){ this.name = name; this.getName = function(){ return this.name; } }この方法は処理が長くなるとコンストラクタが見難くなる。
2.コンストラクタ外で外部関数として宣言しメンバ関数として関連付ける。
function SuperClass(name){ this.name = name; this.getName = getName; } function getName(){ return this.name; }やり方としてはいい感じだけど、関数名のスコープが非常に気になる。
3.クラス名.prototype.関数名() {処理}。
function SuperClass(name){ this.name = name; function SuperClass.prototype.getName(){ return this.name; } }クラス名.prototypeが何なのか不明。
Javascriptで静的アクセス可能なフィールドがあることを初めて知った。
使い方がおかしいのかもしれないが、opera、Firefoxでは動作せず。。。4.コンストラクタ外で外部関数として宣言しクラスの静的prototypeに関連付ける。
function SuperClass(name){ this.name = name; } SuperClass.prototype.getName = function(){ return this.name; } 静的フィールドを使うので無駄な処理は省かれる。 また、静的フィールドに割り当てられるので無名関数を使え、関数名の衝突を気にする必要がなくなる。 しかしグローバル領域で宣言及び代入することになるので、宣言が散ってしまう。
標準的には4の方式がよいとされているらしいが個人的にはJavascriptでクラスのメンバー関数を書くときは1の方式で書くのが良いのではないかと思われる。しかし1、2方式はインスタンスを作成するとその都度コンストラクタでプロトタイプが設定されるため、大量にインスタンスを作成するときは注意が必要。
2の方式もC++チックで結構いい感じだが、Javasciptではネームスペースを宣言できるか不明なのでプロパティ名と実際の関数名を変えるなど自力で何か対策をしないと継承させるときに泣く羽目になる。
3はIEでしか動かないので論外。
4は標準的な方法でお仕事で使う場合。
でもこれ、極端な話アドレスバーからもクラスを拡張できてしまうのでこの機能はどうなんでしょうか。
またvarの使い方には注意が必要。
他のプログラミング言語に慣れていると、クラスの処理を書くときにはメンバー変数として残しておきたい値はthisで、一時的な計算変数はvarで宣言するという感覚で使用するだろう。
しかし、1方式の場合、thisではなくvarでアクセスしても見かけ上は問題なく参照できてしまう。
1方式のメンバー関数の記述の場合、コンストラクタ(newの対象となる関数)内ではできるだけ、var宣言をしないということが重要だ。
一応、問題となるソースを挙げておく。
ex1) function SuperClass() { var value = 1; this.getValue = function () { return value; } this.addValue = function () { value++; } }
ex2) var value = 1; function SuperClass() { this.getValue = function () { return value; } this.addValue = function () { value++; } }
参照している変数スコープが違うので、非常にマズイことが分かる。
まあ、2、4方式の場合でも変数スコープが問題ではあるが、ex1)のような場合値を返さない分、気づきやすいと言える。