値と変数
値の型
他の多くの言語がそうであるように、 JavaScript にも値の型が存在します。
しかし、各言語が作られた目的やフィールドの違いに由来し、型の意味合いも言語毎に大きく異なります。古典的には、型と言えば精度の異なる複数の整数型と実数型を指すものでした。これは CPU が直接に理解できるマシン語の世界観に相当するものですが、この概念自体は古典的な多くの言語においてそのまま踏襲されます。 FORTRAN 然り、 PASCAL 然り、 BASIC 然り、 C 然り。
その後、 LISP を筆頭とする高級言語が台頭するのを皮切りに、こうした物理的な型の区別に加え、より観念的な、あるいは実務的な型の概念が採用されるようになってゆきました。 文字列、ポインタや参照、列挙値、配列や構造体、共用体、未定義値、そしてオブジェクトや関数などです。
JavaScript の型概念は、物理的な数値の区別は一切排除し、その一方でオブジェクトのクラスの違いを型とは別の次元で区別するルールを採用しています。まずは、 JavaScript にどのような型が用意されているのかを、一通り見てみることにしましょう。
数値
JavaScript には整数と実数の区別がありません。もちろん、整数の精度毎の区別もありません。数値として扱う値はすべて、数値型の値として扱われます。
// 以下はすべて数値型の値 var a = 0; var b = 20100802144236; var c = -12.3456; var d = 1.73e-11; var e = Infinity; // 無限大 var f = NaN; // 非数
上記で変数 d に代入している値は、数学的には「1.73×10-11」という意味です。
Infinity は無限大を表す定数です。任意の値を 0 で割った場合にもこの値を得られます。 Infinity で掛けた数値はどんな数値でも Infinity になり、 Infinity で割った数値はどんな数値でも 0 になります。
NaN は非数を表す定数です。例えば負の値の平方根を求めようとしたりすると、この値を得られます。数値型ではありますが、数値ではありませんので、他のどの数値とどのような演算を行っても結果は NaN になります。
数値型の値には、さまざまな数値演算を適用することができます。また、数値同士の足し算は、通常の数値の足し算として計算した結果が得られます。
var a = 12.3 + 45.6; // 57.9 var b = 8901 - 2345 / 67; // 8866 var c = 650 % 7; // 6
数値型の値と文字列型の値の足し算を行った場合、数値を数字の文字列に置き換えた上で、連結された文字列が結果として得られます。
var d = 12.3 + "45.6"; // "12.345.6" var e = "社員数: " + 1; // "社員数: 1" var f = "1 + 1 = " + 1 + 1; // "1 + 1 = 11"
上記の変数 f に代入する例はよくある間違いで、恐らく本来意図した通りの動作ではないでしょう。足し算は数値の足し算でも文字列の連結でも左から順に行うので、このケースでは数値の足し算を括弧で括る必要があります。
var f = "1 + 1 = " + (1 + 1); // "1 + 1 = 2"
文字列
ダブルクォーテーションマーク '"'~'"'、およびシングルクォーテーションマーク "'"~"'" で括った文字列は、文字列値として扱われます。
var a = "Hello, World!!"; var b = 'Goodbye, dream...';
文字列は足し算演算子で連結が可能です。文字列と数値の足し算もまた、数値をその数字の文字列として扱った上で連結されます。
var c = "T.MURACHI は " + 1978 + "年 " + 2 + "月 " + 7 + "日生まれの " + 32 + "歳です。";
文字列中に、 C 言語ライクなエスケープ文字を含めることもできます。
var d = "いろはにほへと\nちりぬるをわか\nよたれそつねな\n" + // \n は改行文字 "らむうゐのおく\nやまけふこえて\nあさきゆめみし\nゑひもせすん"; var e = "氏名:\t村山 俊之\n年齢:\t32\n性別:\t男性"; // \t はタブ文字 var f = "-?(?:\\d+\\.?|\\d*\\.\\d+)(?:e[\\+\\-]?\\d+)?"; // \\ は \ そのもの
真偽値
比較演算の結果が正しいか否かを示す値です。真を表す true と、偽を表す false の 2通りのみを取ります。
var a = true; // 真 var b = false; // 偽 var c = 1 + 2 == 3; // true var d = 4 * 5 < 6; // false
実際には比較演算のみならず、関数の処理が成功したかどうかを表す戻り値や、フラグとして表現されるべきステータス全般などにおいて、広く用いられています。
未定義値
識別子 undefined で表されるこの値は、変数に値が定義されていないことを表すものです。値なのに値が定義されていないとはこれ如何に。
var a = undefined;
上記は以下と完全に同義です。
var a; // 初期値を指定しない変数には undefined が入っている…!!
関数
関数は値である、といわれても、感覚的にはぴんと来ないかも知れません。しかし、関数を変数に代入しておいたり、関数の処理内容を動的に作り出したりできる仕組みは、慣れるとなかなかに便利なものです。
まず、 function 宣言によって定義された関数は、そのままその名前の変数として振る舞います。
function a() { /* 処理... */ } var b = a; // 変数 b に関数 a を代入
このとき、以下のように記述することで、変数 b から関数を実行することができます。
b(); // a(); と等価の処理を実行する
なお、一度定義した関数の処理内容を直接変更することはできません。別の関数を代入して上書きすることはできますが…。
名前をつけてちゃんと定義した関数ではなく、無名の関数をその場で変数に代入することもできます。
var c = function() { /* 処理... */ }; c(); // もちろん、こう書けば処理を実行できる
このような関数定義の書き方は無名関数などと呼ばれたりしています。
関数呼び出しの前に new 命令を挿入すると、その関数をコンストラクタとしたオブジェクトの生成ができるのですが (詳細は後ほど…)、無名関数でもオブジェクトの生成はもちろん可能です。
var X = function() { this.name = "unknown"; this.mask = "secret"; }; var x = new X(); // オブジェクト生成 var y = new (function() { /* ... */ })(); // こんな書き方も一応可能
オブジェクト
JavaScript はオブジェクト指向的な性格の強い言語ですので、未定義値を除くすべての型で、その値にオブジェクトとしての性格を持っています。例えば数値型の値には toFixed() メソッドを適用することで、指定した精度の少数を表す文字列を得ることができますし、
var root2 = 1.41421356; alert(root2.toFixed(3)); // "1.414" と表示
文字列型の値には substr() メソッドを適用することで、指定した位置の文字列を切り出すことができたりします。
var text = "Hello World."; alert(text.substr(6, 5)); // "World" と表示
しかし、より自由に、プログラム側からメンバに新しい値を代入し、それをメンバフィールドやメンバメソッドとして活用することのできる型は、2種類しか存在しません。ひとつは関数型、そしてもう一つがオブジェクト型です。
オブジェクト型の値を生成する方法は以下の 2通りです。
var a = { "foo": 1, "bar": "hoge", "baz": true, "showAll": function() { alert("foo: " + this.foo + ", bar: " + this.bar + ", baz: " + this.baz); } }; var b = new Object();
変数 a に代入している方は、最も手軽にオブジェクト型の値を生成する方法です。全体をブレース "{" ~ "}" でくくり、「"メンバ名": 値」の組み合わせをカンマ "," で区切って記述します。すると、指定したメンバ名の値を持つオブジェクトが生成されます。
変数 b に代入している方は、関数呼び出しをコンストラクタとして利用することにより、オブジェクトを生成する方法です。 "Object" というのは、実は JavaScript に最初から定義されている関数名ですが、自分で定義した関数をコンストラクタに用いることももちろんできます。
// コンストラクタ function X() { this.foo = 1; this.bar = "hoge"; this.baz = true; this.showAll = function() { alert("foo: " + this.foo + ", bar: " + this.bar + ", baz: " + this.baz); }; } // オブジェクトを生成 var x = new X();
ここで、変数 x に代入されたオブジェクトと、先ほどの例で変数 a に代入されたオブジェクトは、内容的には等価です。オブジェクトのメンバには、ドット "." に続いてメンバ名を指定することにより参照することができます。
alert(a.foo); // "1" を表示 alert(a.bar); // "hoge" を表示 alert(a.baz); // "true" を表示 a.showAll(); // "foo: 1, bar: hoge, baz: true" を表示 alert(x.foo); // "1" を表示 alert(x.bar); // "hoge" を表示 alert(x.baz); // "true" を表示 x.showAll(); // "foo: 1, bar: hoge, baz: true" を表示
型を調べる
ある変数に代入されている値がどの型なのかに応じて、処理を振り分けたい場合があります。そのようなときには、値の型を調べる必要があります。型を調べるには、 typeof 演算子を利用します。
var type = typeof x; // 変数 x に代入されている値の型を調べる
ここで、上記の変数 type に代入される値は、以下のいずれかの文字列です。
- "number" ... 数値
- "string" ... 文字列
- "boolean" ... 真偽値
- "undefined" ... 未定義値
- "function" ... 関数
- "object" ... オブジェクト
null という値
ところで、 JavaScript には、 null という名前の値が存在します。他の言語を既に習得されている方にとっては非常に馴染みのある単語かと思われますが、 JavaScript においても、未定義値である undefined と非常によく似た性質を持つ値で、オブジェクトを返す関数が、オブジェクトを返せないことを表すために、代わりに返す値としてよく使われます。
null は undefined と似ているので、等価演算子で比較すると、真を返します。しかし、厳密には違う値なので、厳密等価演算子で比較すると、偽を返します。
// この if-else 文は "null == undefined is TRUE!!" を表示 if (null == undefined) alert("null == undefined is TRUE!!"); else alert("null == undefined is FALSE!!"); // この if-else 文は "null === undefined is FALSE!!" を表示 if (null === undefined) alert("null === undefined is TRUE!!"); else alert("null === undefined is FALSE!!");
また、 null は undefined によく似ていますが、これでも立派なオブジェクト型の値です。
var a = typeof null; // "object"
ただ、オブジェクト型であるにも関わらず、 undefined との近似性を表したいのか、メンバに値を代入しようとしたりすると、エラーになります (例外を送出する)。
null.hoge = "fuga"; // エラー! var b = null.toString(); // これもエラー!!
変数
変数とは、値を 1つだけ保存しておくことができる名前のことです。手続き型の言語であれば必ず存在する、おなじみの概念ですね。
変数の宣言
JavaScript で使用する変数を宣言するには、 var 命令を利用します。以下のように記述すると、 hoge という名前の変数を使いますよ、という宣言になり、以後、そのスコープ内において、変数 hoge を安全に使用することができるようになります。
var hoge; // 変数 hoge を宣言する
変数のスコープ
先ほどの説明において、唐突にスコープという語が出てきたので、戸惑ったかも知れません。スコープとは、その変数を利用できる範囲のことを言います。
JavaScript では、変数を宣言した場所が関数内、即ち関数の処理内容を定義するブロック内のどこかであれば、その変数を宣言した位置から、関数の処理内容を記述するブロック内の末尾までが、その変数のスコープとなります。変数を宣言した場所が、どの関数にも含まれない、全くの外側である場合、その変数を宣言した位置から、プログラム全体の末尾までが、その変数のスコープとなります。
// ...処理... (この辺は変数 hoge のスコープに含まれない) var hoge; // 変数 hoge のスコープはここから始まる // ...処理... (この辺は変数 hoge のスコープに含まれる) function Func() { // ...処理... (この辺は変数 hoge のスコープに含まれる…fuga のスコープには含まれない) var fuga; // 変数 fuga のスコープはここから始まる // ...処理... (この辺は変数 hoge, fuga 両方のスコープに含まれる) } // 関数終了 - 変数 fuga のスコープはここで終わる // ...処理... (この辺は変数 hoge のスコープに含まれる…fuga のスコープには含まれない)
関数は入れ子で定義することができますが、その場合、より内側の関数内で宣言された変数は、その内側にある関数内のみがスコープとなります。
function Func() { var hoge; function SubFunc() { var fuga; // ...処理... (この辺は変数 hoge, fuga 両方のスコープに含まれる) } // ...処理... (この辺は変数 hoge のスコープに含まれる…fuga のスコープには含まれない) }
宣言されていない変数
JavaScript では、まったく宣言されていない名前の変数にいきなり値を代入しても、エラーにはなりませんFootNote(「厳密な !JavaScript 」のオプションがある環境では、コンソールに警告を出力するように設定することができます。例えば Firefox の場合、 about:config にて javascript.options.strict を true に設定しておくと、「ツール」→「エラーコンソール」メニュー選択で表示されるコンソール画面に警告が表示されるようになります。)。この場合、その変数は、グローバルオブジェクトのメンバフィールドとして扱われます。
Web ブラウザに実装された JavaScript エンジンの場合、グローバルオブジェクトは window になりますので、下記のような場合には、
hoge = "fuga"; // この変数 hoge はまだ宣言されていない
以下のように記述するのと同じ意味になります。
window.hoge = "fuga"; // Web ブラウザでは window がグローバルオブジェクト
グローバルオブジェクトは JavaScript エンジンを搭載しているアプリケーションによって異なりますので (それは system かも知れませんし、 global かも知れませんし、あるいはアプリケーションの名前かも知れません)、事前に確認しておいた方がよいでしょう。
なお、まったく宣言されていないし値の代入すら行われていない名前の変数に、既に値が入っているものとして参照しようとした場合には、エラーとなります。
var hoge = fuga; // エラー: 変数 fuga は定義されていない!!
変数の名前
JavaScript では、変数の命名規則は以下の通りになっています。
- 1文字目はアルファベットか、アンダースコア "_"、ドル記号 "$" で始まらなければならない。
- 2文字目以降は上記に加え、さらに数字を用いることができる。
- アルファベットの大文字・小文字は、区別される。
なお、 JavaScript 1.5 以降では、アルファベットは半角英字のみならず、 Unicode でアルファベットとされる文字であればすべて含まれます。例えば日本語のひらがなやカタカナ、漢字などもアルファベットに含まれます。但し、半角英字以外のアルファベットを変数名に用いる場合は、JavaScript プログラムが書かれたファイルを UTF-8 エンコード形式で保存する必要があります。
var abc = "XYZ"; // ok. すべてアルファベットの名前 var result1_value = 20; // ok. 2文字目以降なら数字も使用可 var _RANDOM_SEED = (new Date()).getTime() + 1637291; // ok. "_" で始まる名前 var $coin10Num = 50; // ok. "$" で始まる名前 var 氏名 = "村山 俊之"; // ok. Unicode 的には漢字もアルファベット。 // 但しプログラムファイルは UTF-8 で保存すること var 1st_name = "Toshiyuki"; // NG!! 1文字目に数字を使用することはできない var company-name = "Harapeko Inc."; // NG!! ハイフン "-" は使用不可 var ★重要★ = true; // NG!! Unicode 的に見ても "★" はアルファベットではない
変数への代入
変数に値を代入するには、イコール演算子 "=" を使用します。左辺に変数を、右辺に代入する値を記述します。
a = 1 + 2; // 変数 a に 1 + 2 の演算結果を代入
変数への値の代入は、変数の宣言時に行うこともできます。宣言時に代入を行うことを、特に変数の初期化などと呼んだりします。
var a = 1 + 2; // 変数 a を 1 + 2 の演算結果で初期化する
実は、代入を行う演算子は単なるイコールだけではないのですが、その辺の説明は、さまざまな演算の章に譲ることにします。
変数の型
変数自体が型を持つか否かは、プログラム言語のポリシーに応じて別れるところです。特に、コンパイルを行うことを意識した言語 (C/C++, Java, C#, Pascal/Delphi など) や、事務処理に特化した言語 (COBOL, BASIC など) が変数に型を持たせているのとは対照的に、手軽さを重視したスクリプト言語の類 (Perl, Python, PHP, Ruby など) では変数に型を持たせないのが一般的です。
JavaScript もこの例に漏れず、スクリプト言語ですので、変数に型はありません。なので、例えば数値で初期化した変数に、後から文字列を代入することもできてしまいます。
var a = 32; a = "Toshiyuki Murayama"; // a に元々何が入っていようと、後から何でも代入できる。例え型が異なろうとも。
このような言語の場合、ある特定の型の値が入っていることを想定して処理を行わなければならないようなケースでも、変数にどういった型の値が入ってくるかが予測できない、というようなことが起こり得ます。そのような場合には、変数に格納されている値の型を、typeof 演算子を用いて事前に調べる必要があるでしょう。
// 未定義値または null の場合は何もせずに未定義値を返す if (elem == undefined) return undefined; // 型が文字列、数値、真偽値であれば、DOM のテキストノードに変換する。 if (typeof elem == "string" || typeof elem == "number" || typeof elem == "boolean") elem = document.createTextNode(elem); // Date オブジェクトもロケールの文字列を DOM のテキストノードに変換する。 else if (elem instanceof Date) elem = document.createTextNode(elem.toLocaleString()); // それ以外で DOM ノードじゃない場合、とりあえず toString() を信じてみる。 else if (!(elem instanceof Node)) elem = document.createTextNode(elem.toString());
上記の例では instanceof 演算子が用いられていますが、こちらはオブジェクト型の値について、さらに詳しく種類を調べる為のものです。詳しくはプロトタイプとオブジェクト指向の章を参照してください。