[AS3] Stringはプリミティブ値かオブジェクトか [Edit]

文字列は単純な値でしょうか、それともオブジェクトでしょうか。実は少し込入った仕組みになっていて、しかもActionSctipt 1.0/2.0と3.0とで仕様が変わっています。

1. ActionScript 1.0/2.0の場合

ActionScript 1.0/2.0では、文字列はプリミティブと呼ばれる単純な値です[*1]。しかし、文字列はStringクラスのメソッドで操作したり、プロパティを調べることができます。たとえば、つぎのスクリプト001は、"f-site"という文字列の長さを出力します。

スクリプト001■文字列の長さを調べる

var _str:String = "f-site";
trace(_str.length);   // 出力: 6

プリミティブ値というのは、「基本データ型」とも呼ばれ、本来プロパティやメソッドをもちません[*2]。しかし、文字列に対してプロパティやメソッドにアクセスすると、その文字列に対応したStringクラスのインスタンスが自動的に生成され、文字列をStringインスタンスであるかのように扱う仕組みになっているのです[*3]

文字列に対してプロパティやメソッドを問合せると、ステージママよろしくStringオブジェクトがしゃしゃり出てくるということです。そして、文字列に替わって勝手に仕事をし終わると、また引込みます。ステージに上がるのは、あくまで文字列です。

それでは、Stringインスタンスと文字列は変わりがないかというと、そうではありません。以下のスクリプト002は、引数なしで生成したStringインスタンスと空文字列のプリミティブ値との違いを確かめています。

スクリプト002■Stringインスタンスと文字列の比較

var object_str:String = new String();
trace([object_str, typeof object_str, object_str instanceof String, object_str.length]);
// 出力: ,object,true,0
var value_str:String = "";
trace([value_str, typeof value_str, value_str instanceof String, value_str.length]);
// 出力: ,string,false,0
trace(object_str == value_str);   // 出力: true

どちらも出力すると空文字列で、長さは0です。しかし、typeof演算子でデータタイプを調べるとオブジェクトは"object"、プリミティブ値は"string"と評価されます。instanceof演算子でStingインスタンスかどうかを調べると、オブジェクトはもちろんtrueを返すものの、文字列はfalseという評価になります。

これはたとえば、つぎのスクリプト003のような処理で違いを生じます。空文字列を設定した変数value_str について条件として論理値評価すると、Flash Player 7以降は文字列の長さが0の場合はfalse(それ以外はtrue)になります。したがって、スクリプト003は、"テキストなし"と出力します。

スクリプト003■変数に空でないテキストが設定されているかを判定

// if (object_str) {
if (value_str) {
  trace("テキストあり");
} else {
  trace("テキストなし");
}

ところが、Stringインスタンスの参照を格納した変数object_str は、オブジェクトであるため、論理値としてはtrueと評価されることになります。この結果は、誤解を招きやすいといえます。

また変数値が文字列プリミティブかStringインスタンスであることを判定するには、typeofinstanceofのふたつの演算子を用いなければなりません。そう考えるとActionScript 1.0/2.0では、new演算子でStringコンストラクタによりインスタンスを生成するのは避け、文字列のプリミティブ値のみを用いる方がよいように思われます。


[*1] Flash 8オンラインヘルプ[ActionScript 2.0の学習] > [データおよびデータ型] > [データ型について] > [プリミティブデータ型および複合データ型について]参照。

[*2] データ型の種類とそれらの特徴については、Flash ActionScript Helper「データ型」参照。

[*3] ActionScriptと同じECMAScriptに準拠するJavaScriptについて、David Flanagan『JavaScript第3版』(O'REILLY) p.52〜は、Stringクラスは基本データ型である文字列を包込むラッパーだと述べます。そして、つぎのように説明しています。「指定された文字列のプロパティやメソッドにアクセスすると、JavaScriptがその文字列に対応したStringラッパーオブジェクトを内部的に生成します。そして、文字列の代わりに、Stringオブジェクトを使用します」。

2. ActionScript 3.0の場合

ActionScript 3.0では、オブジェクトとプリミティブ値のふるまいが統一されました(スクリプト004)。どちらも、typeof演算子で"string"を返し、instanceof演算子によるStringインスタンスとしての評価はtrueになります。さらに、ふたつを厳密な等価演算子===で比較した場合にもtrueを返します。

スクリプト004■Stringインスタンスと文字列の扱いが統一

var object_str:String = new String();
trace([object_str, typeof object_str, object_str instanceof String, object_str.length]);
// 出力: ,string,true,0
var value_str:String = "";
trace([value_str, typeof value_str, value_str instanceof String, value_str.length]);
// 出力: ,string,true,0
trace(object_str === value_str);   // 出力: true

これは、ActionScript 3.0が準拠するECMAScript 4では、オブジェクトとプリミティブ値が区別されなくなったためです[*4]。ただし、この変更はプロパティやメソッドへのアクセスについてです。代入や論理比較の際、参照でなく値として扱われるというプリミティブ値の重要な性質は残されています。たとえば、以下のスクリプト005は、配列(Array)と文字列(String)のそれぞれについて、代入と論理比較を行った例です。

スクリプト005■配列と文字列の代入と論理比較

var a_array:Array = new Array(0, 1, 2);
var b_array:Array = new Array(0, 1, 2);
var c_array:Array = a_array;
trace(a_array === b_array);   // 出力: false
trace(a_array === c_array);   // 出力: true
var a_str:String = new String("abc");
var b_str:String = new String("abc");
trace(a_str === b_str);   // 出力: true

Arrayインスタンスを作成して変数に代入すると、変数にはインスタンスへの参照が格納されます。その変数値を他の変数に代入した場合も、インスタンスへの参照が渡されます。クラスのインスタンス同士を等価比較した場合、インスタンスの参照が一致しなければtrueと評価されず、値となるエレメントすべてが完全に一致してもfalseが返されます。

これに対して、Stringは値が代入され、等価比較も値として評価されます。したがって、別途に作成したふたつのインスタンスを等価比較した場合も、両者の値が等しければtrueが返されます。このふるまいは、プリミティブとしての性質を残しているといえます。

ActionScript 3.0では、変数にデフォルト値が定められました。String型データのデフォルト値は、nullです。これは、クラスのデータの場合と同じ値です。しかし、nullは文字列として扱おうとすると不都合があります。

以下のスクリプト006は、String型の変数に初期値を与えておらず、nullとして処理されます。すると、nullに対してStringクラスのプロパティやメソッドにアクセスできず、コンパイルエラーを生じます。

スクリプト006■Stringのデフォルト値に対する処理結果

var _str:String;
trace(_str);
trace(String(_str));   // 出力: null
// trace(_str.toString());   // コンパイルエラー
// trace(_str.length);   // コンパイルエラー

Javaの仕様では、String型変数のデフォルト値は、やはりnullとされているようです。しかし、JavaはStringをクラスのインスタンスとして扱っており、代入も論理比較もインスタンスへの参照を対象として行われます[*5]。クラスで型指定された変数は、まだインスタンス化されていないとき、つまり参照を保持していなければ、そのクラスのプロパティやメソッドにアクセスできないことも理解できます。

けれども、文字列は前述のとおりプリミティブ値としての性質を保持しています。同じプリミティブ出身のBooleanやNumberあるいはintやuintが、プロパティやメソッドにアクセス可能なデフォルト値をもつのと比べると、合理的な扱いとはいえないように思われます。

少なくとも実際問題として、String型の変数がデフォルト値のnullのまま処理できるケースはあまり考えにくく、結局ActionScript 3.0では空文字列""でつねに初期値を与えておくべきだということになるでしょう。


[*4] ECMAScript 4については、「ActionScriptとその基本概念について」「06 ActionScript 2.0とFlash Player 7」を参照。

ECMAScript 4「Types」(型)には、ECMAScript 4は「ECMAScript 3と異なり、オブジェクトとプリミティブ値との区別はありません。すべての値は、メソッドをもつことができます」とされています。

また、ActionScript 3.0でも(「Flash Programming ActionScript 3.0」の[Overview of ActionScript Programming] > [ActionScript Language and Syntax] > [Data types]参照)、「ActionScript 3.0のすべての値は、プリミティブであっても複合データ(オブジェクト)型であっても、オブジェクトです」としています。

[*5] たとえば、Java入門「文字列」参照。

コメント

この記事にコメントを書く

記事に対するテクニカルな質問はご遠慮ください(利用規約)。

その他の記事