結論から言えば以下のような方法で判定することができる。
function IsString(value)
{
return typeof value === "string" || value instanceof String;
}
console.log("\"リテラル文字列\" : " + IsString("リテラル文字列"));
console.log("new String(\"文字列\") : " + IsString(new String("文字列")));
console.log("123 : " + IsString(123));
console.log("true : " + IsString(true));
console.log("null : " + IsString(null));
console.log("undefined : " + IsString(undefined));
このようにその変数のtypeof
が"string"
であるか、あるいはその変数に対してinstanceof String
がtrue
を返すか、いずれかを満たせば文字列であると判別できる。
上記のように少々回りくどい方法をとる必要があるのはJavaScriptにおける文字列の内部表現が関係している。
JavaScriptでは文字列を扱う方法として次の2種類の方法がある。
let primitiveString = "プリミティブ文字列";
let objectString = new String("オブジェクト文字列");
ざっくり言えば、プリミティブな値とはメソッドやプロパティを持たない純粋なデータのこと。 対してオブジェクトはそのデータを扱うためのプロパティやメソッドを持っているもの。 これらはソースコード上でほぼ同じものとして扱うことができるが、厳密には区別されている。
let primitiveString = "プリミティブ文字列";
let objectString = new String("オブジェクト文字列");
console.log("typeof primitiveString: " + (typeof primitiveString));
console.log("typeof objectString: " + (typeof objectString));
console.log("primitiveString instanceof String: " + (primitiveString instanceof String));
console.log("objectString instanceof String: " + (objectString instanceof String));
このようにtypeof
やinstanceof String
を使用するとその違いが見えてくる。
しかし実際はプリミティブ型の文字列に対して以下のように直接メソッドを呼び出すことができる。
console.log("0123456789".substr(2, 4));
これはプリミティブ型の値に対してメソッドの呼び出しが記述された場合、実行時に自動的にString型のオブジェクトを生成してそこからメソッドを呼び出しているからだ。
しかし文法上同じように扱えるのは内部的にそう処理してくれているからであって、実際にはプリミティブ型のデータとオブジェクト型のデータは異なる状態で存在している。
そのためtypeof
やinstanceof
の結果は異なるものになる。
このことは文字列だけではなく、他のプリミティブ型に対しても同様となる。
プリミティブな数値に対するNumber
やBigInt
、真偽値であればBoolean
といったように、プリミティブな値にはそれに対応するラッパークラスが用意されている。
詳しくは以下のページに詳しく書かれているので参照されたい。
普段あまり意識しないことだが、プリミティブ型とそれに対応するラッパークラスの関係はしっかり理解しておかないと気付きにくいバグを生む場合がある。 JavaScriptは時々「変数に型が無い」といった言われ方をされるが、意識しづらいだけで厳密に型が存在する。 値の比較や変換で自動的にあれこれやってくれるからといって、それに依存せずきちんとデータのあり方を意識していく必要があるだろう。