|▽ ̄)ノ なページ再帰 - UJS/nitpicking
JScript を使う >> 重箱の隅

重箱の隅


Windows XP pro. sp2 上で、次のバージョンで確認しています。
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

目次

Class


JScript のクラスは関数オブジェクトです。 new 演算子を使いインスタンスを生成します。継承は prototype プロパティに親クラスのオブジェクトを代入し作成します。デストラクタは(おそらく)ありません。// class.inheritance.js

インスタンスの生成に作成パラメータを指定できます。子クラスで受け取った作成パラメータを親クラスのコンストラクタへ渡す場合は、子クラスのコンストラクタで親クラスのコンストラクタを呼び出す必要があります。親クラスのコンストラクタを呼び出すには call メソッドを使用します。引数を配列で渡す場合は apply メソッドを使います。// class.initialize.call.js

constructor プロパティはインスタンスのコンストラクタ関数オブジェクトを返します。このプロパティを介して新たにインスタンスを作成できます。ただし JScript 5.6 では基底クラスが返ります。 JScript 5.6 で子クラスのインスタンスを作成するには prototype.constructor にコンストラクタを設定します。// class.constructor.js

public な属性やメソッドは this キーワードに付加する形で定義します。サンプルにはありませんがメソッドのオーバーライドも出来ます。// class.js

prototype プロパティを使用すると既存のクラスに属性やメソッドを付加できます。使い方は this キーワードと同じです。クラスの初期化中に自身を修飾することは出来ません。

無名クラスも作成できます。// class.unnamedClass.js

親クラスの属性およびメソッドは子クラスから見えます。// class.scope.js

コンストラクタのローカル変数は保持されるようです。親クラスから子クラスのローカル変数は見えません。プライベート変数らしい。// class.initialize.scope.super.js

子クラスから親クラスのローカル変数は見えません。継承したメソッドから値を受け取れます。オーバーライドすると受け取れなくなります。

親クラスは子クラスのメソッドを通してローカル変数の値を受け取れます。// class.initialize.scope.sub.js

ローカルで宣言したクラスからローカル変数が見えます。サンプルは無名クラスの例を含みます。// class.localclass.scope.js

コンストラクタで宣言した関数はプライベート メソッドになります。// class.private.js
// class.inheritance.js
function SuperClass() {}

function SubClass() {}
SubClass.prototype = new SuperClass;

o = new SuperClass;
p = new SubClass;
WScript.echo( o instanceof SuperClass );    // true
WScript.echo( p instanceof SuperClass );    // true
WScript.echo( o instanceof SubClass );      // false
WScript.echo( p instanceof SubClass );      // true
// class.initialize.call.js
function Super( i )
{
    this.mSuper = ( "init: " + i );
}

function Sub( i )
{
    Super.call( this, ( "arg " + i ) );
    this.mSub = ( "init: " + i );
}
Sub.prototype = new Super;

o = new Sub( "new" );
WScript.echo( o.mSuper );   // init: arg new
WScript.echo( o.mSub );     // init: new
// class.constructor.js
function Super() {}

function Suba() {}
Suba.prototype = new Super;

function Subb() {}
Subb.prototype = new Super;
Subb.prototype.constructor = Subb;

o = new Suba;
p = new Subb;
q = new o.constructor;
r = new p.constructor;
WScript.echo( o.constructor );      // function Super() {}
WScript.echo( p.constructor );      // function Subb() {}
WScript.echo( q.constructor );      // function Super() {}
WScript.echo( r.constructor );      // function Subb() {}
WScript.echo( o instanceof Super ); // true
WScript.echo( o instanceof Suba );  // true
WScript.echo( p instanceof Super ); // true
WScript.echo( p instanceof Subb );  // true
WScript.echo( q instanceof Super ); // true
WScript.echo( q instanceof Suba );  // false
WScript.echo( r instanceof Super ); // true
WScript.echo( r instanceof Subb );  // true
// class.js
function SuperClass()
{
    this.attr = "hi";
    this.hi = 
        function()
        {
            return this.attr;
        };
}

function SubClass()
{
    this.hello = 
        function()
        {
            return "hello";
        };
}
SubClass.prototype = new SuperClass;

p = new SubClass;
WScript.echo( p.hi() );     // hi
WScript.echo( p.hello() );  // hello
// class.unnamedClass.js
klass = 
    function()
    {
        this.hi = 
            function()
            {
                return this.attr;
            };
    };

superKlass = klass;

subKlass = 
    function()
    {
        this.hello = 
            function()
            {
                return "hello";
            };
    };
subKlass.prototype = new superKlass;

superKlass.prototype.attr = "hi";
o = new klass;
p = new superKlass;
q = new subKlass;
WScript.echo( o.hi() );     // hi
WScript.echo( p.hi() );     // hi
WScript.echo( q.hi() );     // hi
WScript.echo( q.hello() );  // hello
p.attr = "hallo"
WScript.echo( o.hi() );     // hi
WScript.echo( p.hi() );     // hallo
WScript.echo( q.hi() );     // hi
// class.scope.js
function SuperClass()
{
    this.attr = "hi";
    this.hi = 
        function()
        {
            return this.attr;
        };
}

function SubClass()
{
    this.hello = 
        function()
        {
            return this.attr;
        };
}
SubClass.prototype = new SuperClass;

p = new SubClass;
WScript.echo( p.hi() );     // hi
WScript.echo( p.hello() );  // hi
// class.initialize.scope.super.js
function Super()
{
    var hi = "hi";
    var hello = "hello";
    this.foo = 
        function()
        {
            return hi;
        };
    this.bar = this.foo;
    this.baz = 
        function()
        {
            return hello;
        };
}

function Sub()
{
    var hello = "HI";
    this.bar = 
        function()
        {
            return hi;
        };
}
Sub.prototype = new Super;

hi = "...";
o = new Sub;
WScript.echo( o.foo() );    // hi
WScript.echo( o.bar() );    // ...
WScript.echo( o.baz() );    // hello
// class.initialize.scope.sub.js
function Super()
{
    this.foo = 
        function()
        {
            return hi;
        };
    this.bar = this.foo;
    this.baz = 
        function()
        {
            return this.bar();
        };
}

function Sub()
{
    var hi = "hi";
    this.bar = 
        function()
        {
            return hi;
        };
}
Sub.prototype = new Super;

hi = "...";
o = new Sub;
WScript.echo( o.foo() );    // ...
WScript.echo( o.bar() );    // hi
WScript.echo( o.baz() );    // hi
// class.localclass.scope.js
function hoge()
{
    var v = "local";
    var o = new function()
        {
            var w = "class local";
            this.foo = 
                function()
                {
                    return v;
                };
            this.bar = 
                function()
                {
                    return w;
                };
        };
    return Array( o.foo(), o.bar() );
}

v = w = "...";
a = hoge();
WScript.echo( a.shift() );  // local
WScript.echo( a.shift() );  // class local
// class.private.js
function Klass()
{
    this.foo = 
        function()
        {
            return bar();
        }
    function bar()
    {
        return v;
    }
    var v = "private";
}

k = new Klass;
WScript.echo( k.foo() );    // private
//WScript.echo( k.bar() );  // error

Class Method


クラス メソッドは関数オブジェクトに付加する形で定義します。下の例では methodE です。// class.classmethod.js

クラス メソッドは継承されません。// class.classmethod.inheritance.js
// class.classmethod.js
function Klass()
{
    this.methodA = function() {};
//  this.prototype.methodB = function() {};
    this.constructor.prototype.methodC = function() {};
}
Klass.prototype.methodD = function() {};
Klass.methodE = function() {};

WScript.echo( typeof( Klass.methodA ) );    // undefined
//WScript.echo( typeof( Klass.methodB ) );  // 
WScript.echo( typeof( Klass.methodC ) );    // undefined
WScript.echo( typeof( Klass.methodD ) );    // undefined
WScript.echo( typeof( Klass.methodE ) );    // function
o = new Klass;
WScript.echo( typeof( o.methodA ) );        // function
//WScript.echo( typeof( o.methodB ) );      // 
WScript.echo( typeof( o.methodC ) );        // function
WScript.echo( typeof( o.methodD ) );        // function
WScript.echo( typeof( o.methodE ) );        // undefined

WScript.echo( typeof( Klass.methodA ) );    // undefined
//WScript.echo( typeof( Klass.methodB ) );  // 
WScript.echo( typeof( Klass.methodC ) );    // undefined
WScript.echo( typeof( Klass.methodD ) );    // undefined
WScript.echo( typeof( Klass.methodE ) );    // function
// class.classmethod.inheritance.js
function Klassa() {}
Klassa.methodA = function() {};
function Klassb() {}
Klassb.prototype = new Klassa;
Klassb.methodB = function() {};

WScript.echo( typeof( Klassa.methodA ) );   // function
WScript.echo( typeof( Klassa.methodB ) );   // undefined
WScript.echo( typeof( Klassb.methodA ) );   // undefined
WScript.echo( typeof( Klassb.methodB ) );   // function

this


トップレベルにおける this キーワードはグローバルで宣言された関数および変数を含んでいます。オブジェクト インスタンスから切り離されたメソッドの this キーワードはトップレベルにおける this キーワードと同じ、らしい。クロージャにおける this も同様です。メソッドの移植( mix-in )ができるらしい。// this.context.js
// this.context.js
function KlassA() {}
var value = "global";
this.value = "this";
for( var i in this )
{
    WScript.echo( i + ": " + this[ i ] );
}

"abc".replace( /./, 
    function()
    {
        WScript.echo( "in replace: " + this.value );    // this
    } );

function KlassB()
{
    this.value = "Klass B";
    this.foo = 
        function()
        {
            return this.value;
        };
    function KlassC()
    {
        this.value = "Klass C";
        this.foo = 
            function()
            {
                return this.value;
            };
    }
    this.bar = 
        function()
        {
            var o = new KlassC;
            var m = o.foo;
            return m();
        };
    this.baz = 
        function()
        {
            var o = new KlassC;
            this.m = o.foo;
            return this.m();
        };
}
o = new KlassB;
WScript.echo( "o.foo(): " + o.foo() );          // Klass B
b = o.foo;
WScript.echo( "method object(?): " + b() );     // this
WScript.echo( "o.bar(): " + o.bar() );          // this
WScript.echo( "o.baz(): " + o.baz() );          // Klass B

Scope

グローバル変数とローカル変数


var キーワードにより宣言されなかった変数はグローバル変数になります。ローカル変数として使用する場合は必ず var キーワードにより宣言します。特に注意しなければならないのは for 構文です。ローカル変数として宣言されずに for 式で初期化した制御変数はグローバル変数として使用され思わぬ副作用を招きます。// scope.var.js
// scope.var.js
function foo()
{
    for( i = 1; i <= 10; i++ )
    {
        WScript.echo( i );
    }
}
function bar()
{
    var i;
    for( i = 1; i <= 10; i++ )
    {
        WScript.echo( i );
    }
}
function baz()
{
    for( var i = 1; i <= 10; i++ )
    {
        WScript.echo( i );
    }
}
i = 100;
foo();
WScript.echo( "value of " + i );    // value of 11
i = 100;
bar();
WScript.echo( "value of " + i );    // value of 100
i = 100;
baz();
WScript.echo( "value of " + i );    // value of 100

Function とローカル変数


ローカル変数は外部関数には隠されますが、ローカルで作成した関数オブジェクトには見えます。

ローカルで作成した関数オブジェクトの例。// function.scope.incall.js
// function.scope.outcall.js
function sub()
{
    v = "sub";
}

function foo()
{
    var v = "foo";
    sub();
    return v;
}

function bar( iFunc )
{
    var v = "bar";
    iFunc();
    return v;
}

function baz()
{
    var v = "baz";
    return Array( sub, v );
}

function getfunc()
{
    var v = "getfunc";
    var f = 
        function()
        {
            v = "func in getfunc";
        };
    return f;
}

function bab()
{
    var v = "bab";
    var f = getfunc();
    f();
    return v;
}

v = "top";
r = foo();
WScript.echo( v + " : " + r );  // sub : foo
v = "top";
r = bar( sub );
WScript.echo( v + " : " + r );  // sub : bar
v = "top";
a = baz();
f = a.shift();
r = a.shift();
f();
WScript.echo( v + " : " + r );  // sub : baz
v = "top";
r = bab( sub );
WScript.echo( v + " : " + r );  // top : bab
// function.scope.incall.js
function foo()
{
    var v = "foo";
    var f = 
        function()
        {
            v = "func in foo";
        };
    f();
    return v;
}

function sub( iFunc )
{
    var v = "sub";
    iFunc();
}

function bar()
{
    var v = "bar";
    var f = 
        function()
        {
            v = "func in bar";
        };
    sub( f );
    return v;
}

function baz()
{
    var v = "baz";
    var f = 
        function()
        {
            v = "func in baz";
        };
    return Array( f, v );
}

r = foo();
WScript.echo( r );  // func in foo
r = bar( sub );
WScript.echo( r );  // func in bar
a = baz();
f = a.shift();
r = a.shift();
f();
WScript.echo( r );  // baz

eval とスコープ


eval 関数によって定義された関数はそのスコープ内にとどまります。// eval.scope.js
// eval.scope.js
eval( "function foo(){};" );
(function()
{
    eval( "function bar(){};" );
})();
WScript.echo( typeof( foo ) );  // function
WScript.echo( typeof( bar ) );  // undefined

Closure


関数呼び出しでローカル関数オブジェクトを引数として渡すことができます。このローカル関数オブジェクトをクロージャと呼びます。

クロージャ、ローカルクラス、ローカル変数


呼び出し先の関数内部でローカルクラスのインスタンスを作成しクロージャに渡した場合、このインスタンスのスコープは次のようになります。クロージャの外で bar メソッドを呼び出した場合の挙動が不思議。 varB は Klass のインスタンス変数になるわけではないようです。// closure.localclass.js
// closure.localclass.js
function Klass()
{
    var varA = "class";
    var varC = "class";
    this.method = 
        function( c )
        {
            var varB = "method";
            var k = 
                function()
                {
                    var varC = "local class";
                    this.foo = 
                        function()
                        {
                            return varA;
                        };
                    this.bar = 
                        function()
                        {
                            return varB;
                        };
                    this.baz = 
                        function()
                        {
                            return varC;
                        };
                };
            c( new k );
        };
}
( function()
    {
        var s = new Klass;
        var p;
        s.method(
            function( o )
            {
                WScript.echo( o.foo() );    // class
                WScript.echo( o.bar() );    // method
                WScript.echo( o.baz() );    // local class
                p = o;
            }
        );
                WScript.echo( p.foo() );    // class
                WScript.echo( p.bar() );    // method
                WScript.echo( p.baz() );    // local class
    }
)();

クロージャ、continue 、break


クロージャの外へ continue および break することはできません。// closure.continue.js
// closure.continue.js
a = [ 1, 2, 3 ]
b = function( iBlock ) { iBlock(); }

for( var i in a )
{
    var item = a[ i ];
    b( function()
    {
        if ( 2 === item ) { continue; }         // error
        if ( 3 === item ) { break; }            // error
    } );
    WScript.echo( item );
}

Type

typeof と in


typeof オペレータは与えた引数の型を返します。値は文字列で、 number, string, boolean, object, function, undefined いずれかが返されます。引数が null, Number, String, Boolean, Object, Array, RegExp, property である場合に typeof は object を返します。// typeof.js

in オペレータはプロパティの有無を検査します。右辺のオブジェクトに左辺のプロパティが存在すれば真を、存在しなければ偽を返します。右辺の値によっては例外が発生します。右辺が number, string, boolean, undefined および null であった場合はエラーになります。ただし Number, String, Boolean クラスのインスタンスはエラーになりません。// in.js
// typeof.js
WScript.echo( typeof( 1 ) );                // number
WScript.echo( typeof( "a" ) );              // string
WScript.echo( typeof( true ) );             // boolean
WScript.echo( typeof( function(){} ) );     // function
WScript.echo( typeof( null ) );             // object
WScript.echo( typeof( undefined ) );        // undefined
WScript.echo( typeof( new Number ) );       // object
WScript.echo( typeof( new String ) );       // object
WScript.echo( typeof( new Boolean ) );      // object
WScript.echo( typeof( new Object ) );       // object
WScript.echo( typeof( /abc/ ) );            // object
WScript.echo( /abc/ instanceof RegExp );    // -1
WScript.echo( typeof( [1, 2] ) );           // object
WScript.echo( [1, 2] instanceof Array );    // -1
WScript.echo( typeof( {one:1} ) );          // object
WScript.echo( {one:1} instanceof Array );   //  0

WScript.echo( "\n*** Property: " );
WScript.echo( "prototype" in {a:1} );     //  0
WScript.echo( "prototype" in new Object );  //  0
WScript.echo( "prototype" in Object );      // -1
WScript.echo( "constructor" in {a:1} );       // -1
WScript.echo( "constructor" in new Object );    // -1
WScript.echo( "constructor" in Object );        // -1
WScript.echo( {one:1}.constructor );        // func...
WScript.echo( (new Object).constructor );   // func...
// in.js
m = "method";
//WScript.echo( m in 123 );
o = new Number;
WScript.echo( m in o );
//WScript.echo( m in "abc" );
o = new String;
WScript.echo( m in o );
//WScript.echo( m in true );
o = new Boolean;
WScript.echo( m in o );
WScript.echo( m in function(){} );
WScript.echo( m in /re/ );
//WScript.echo( m in null );
//WScript.echo( m in undefined );
WScript.echo( m in new Array );
WScript.echo( m in Object );
o = { method : 1 };
WScript.echo( m in o );
a = [1];
WScript.echo( "0" in a );

Exception Handling


JScript の例外処理は try-catch-finnaly および throw により行います。 try-catch-finally は try 節で例外が発生すると catch 節が呼び出されます。 catch は型を指定できず、try-catch-finally に一つだけ記述できます。 finally 節は例外の有無にかかわらず実行されます。 throw は例外を上げるために使用し、引数にさまざまな型を与えられます。

JScript は継続実行不可能な状況に遭遇したとき例外を上げます。この例外は Error 型 であり、いくつかのプロパティを持ちます。 name プロパティは例外の種類についてヒントを与えます。 number プロパティはエラーの内容を番号で返します。 description および message はエラーの内容を返します。

Error オブジェクトはユーザが任意で作成できます。 Error 型は作成パラメータにエラー番号と内容を指定でき、それぞれ name, description ( message ) プロパティに相当します。作成パラメータは省略できます。作成した Error オブジェクトは throw により送出します。
// throw.try-catch-finally.js
try
{
    try
    {
        null.method();
    }
    catch( e )
    {
        WScript.echo( "catch: " + e.message );
        throw {message:"throw"};
    }
    finally
    {
        WScript.echo( "finally" );
    }
}
catch( e )
{
    WScript.echo( "catch: " + e.message );
}
// throw.error.js
function print_e( e )
{
    WScript.echo( "description:\t" + e.description );
    WScript.echo( "message:\t" + e.message );
/*
name には以下の値が含まれる、らしい。
    ConversionError
    RangeError
    ReferenceError
    RegExpError
    SyntaxError
    TypeError
    URIError
 */
    WScript.echo( "name:\t\t" + e.name );
    WScript.echo( "number:\t\t" + e.number );
    WScript.echo( "constructor:\t" + e.constructor );
    WScript.echo( "collectiopn: " );
    for( var i in e )
    {
        WScript.echo( "> " + i + ": " + e[i] );
    }
    WScript.echo();
}

try {
    null.method();
} catch( e ) { print_e( e ); }

try {
    throw new Error;
} catch( e ) { print_e( e ); }

try {
    throw new Error( 0, "except" );
} catch( e ) { print_e( e ); }

try {
    throw "except";
} catch( e ) { print_e( e ); }

try {
    throw { name:"name" };
} catch( e ) { print_e( e ); }

不具合

null

null オブジェクトへの操作をキャッチできない。エラー メッセージは理解不能。
// interpreter.error.null.js
try
{
    null.aaa = 1;
}
catch( e )
{
    throw 1;
}
// interpreter.error.null.js(5, 2) Microsoft JScript 実行時エラー: catch ステー
// トメントでは適用されますが、throw ステートメントでは適用されません。

Hash

key


ハッシュのキーにオブジェクトを渡した場合は、 toString で得られた値をキーとして使うらしい。
// hash.object.js
h = new Array;
h[ 1 ] = "number";
h[ "1" ] = "string";
a = new String( "1" );
h[ a ] = "String";
b = new String( "1" );
h[ b ] = "STRING";
c = new String( "01" );
h[ c ] = "STRING 01";
d = /abc/;
h[ d ] = "regexp abc";
e = /ABC/;
h[ e ] = "regexp ABC";
function foo( arg )
{
    var v = arg;
    this.toString = 
        function()
        {
            return "" + v;
        };
}
f = new foo( 1 );
h[ f ] = "foo a";
g = new foo( 2 );
h[ g ] = "foo b";
WScript.echo( h[ 1 ] );     // STRING
WScript.echo( h[ "1" ] );   // STRING
WScript.echo( h[ a ] );     // STRING
WScript.echo( h[ b ] );     // STRING
WScript.echo( h[ c ] );     // STRING 01
WScript.echo( h[ d ] );     // regexp abc
WScript.echo( h[ e ] );     // regexp ABC
WScript.echo( h[ f ] );     // foo a
WScript.echo( h[ g ] );     // foo b
h[ 01 ] = "01";
WScript.echo( h[ 1 ] );     // 01
WScript.echo( a == b );     // false
WScript.echo( a === b );    // false
WScript.echo( a == a );     // true
WScript.echo( a === a );    // true

todo

  • new Array の挙動。
  • 他にもなにかあったような気がするが、思い出せ > 自分

更新履歴


2007/6/23sat 「Exception Handling/不具合/null」、「Hash/key」を追記。
2007/5/12sat 「typeof と in」に追記。「this」を追記。「Exception Handling」のサンプルを修正。
2007/5/7mon 読みやすく(?)構成しなおした。「クロージャ、continue 、break」、「typeof と in 」、「例外処理」を追記。
2007/4/22sun Class に追記。eval とスコープ追記。ページ分割。
2007/4/15sun Class に追記。クロージャとローカルクラスとローカル変数を追記。リンクに JavaScript の資料ともろもろを追記。
2007/3/25sun Class に constructor プロパティ、クラス メソッドを追記。リンク追記。
2007/3/24sat Class に call メソッドを追記。リンク追記。
2007/3/21wed ページ作成。