【JavaScript】制御命令まとめ
ちょっと多かったし時間かかったから混乱しているので
復習がてらにまとめてみるよ
プログラムには3つの構造がある
- 記述された順番に処理を行っていく順次(順接)
- 条件によって処理を分岐する選択
- 単純分岐を記述する(if命令)
- 多岐分岐を記述する(switch命令)
- 特定の処理を繰り返し実行する反復
- 条件式によってループを制御する(while/do..while命令)
- 指定回数だけループを処理する(for命令)
- 配列内の要素を順に処理する(for..in命令)
単純分岐を記述する(if命令)
if (条件式1) { 条件式1がtrueの場合に実行する命令 } [else if (条件式2) { 条件式2がtrueの場合に実行する命令 } [else { 全ての条件式がfalseの場合に実行する命令 }]]
- 「もし~だったら‥、さもなくば‥」という構造を表現するためのもの
- 与えられた条件式のtrue/falseという戻り値によって対応する命令(群)を実行する
- if命令の場合、複数の条件に合致する場合でも合致した最初のブロックのみ実行される(条件の順番には気をつけろ)
多岐分岐を記述する(switch命令)
switch (式) { case 値1 : 「式 = 値1」である場合に実行される命令(群) [case 値2 : 「式 = 値2」である場合に実行される命令(群) [case 値N : 「式 = 値N」である場合に実行される命令(群) ... [default : 式の値がすべての値に合致しない場合に実行される命令(群)]]] }
- 先頭の式がまず評価され、その値に一致するcaseブロックを実行する
- 一致するcaseブロックが見つからない場合には、最終的にdefaultブロックを呼び出す
- defaultブロックは任意だがどのcaseブロックにも一致しなかった場合の挙動を明確にするためにも省略しないことを推奨する
- switch命令は合致するcaseブロックに処理を移動するだけでcaseブロックを終了したところで自動的にswitchブロックから脱出するしくみは備えていない
- break命令を記述して処理中のcaseブロックから処理を脱出するようにするのが一般的
- break命令を指定していない場合、次のcaseブロックが続けて実行されてしまうので意図した結果が得られないので注意
条件式によってループを制御する(while/do..while命令)
while (条件式) { 条件式がtrueである時に実行される命令(群) }
do { 条件式がtrueである時に実行される命令(群) } while (条件式);
- あらかじめ与えられた条件式がtrueである間ループを繰り返す
- while命令
- ループの最初で条件式を判定する(前置判定)
- 条件によっては一度もループを実行しない
- do..while命令
- ループも最後で条件式を判定する(後置判定)
- 条件に関わらず、最低1回はループを実行する
- セミコロンを忘れやすいので注意
- この違いは「ループが開始される前から条件式がfalseである場合」に結果として現れる
指定回数だけループを処理する(for命令)
for (初期化式; ループ継続条件式; 増減式) { ループ内で実行する命令(群) }
- forブロックに入った最初のループで初期化式を一回だけ実行する
- 一般的にはこの初期化式でfor命令によるループ回数を管理するカウンタ変数を初期化する
- ループ継続条件式はブロック内の処理を継続するための条件式を表す
- ブロック内の処理が1回実行されるたびに増減式が実行される
- これらの式には基本的に任意の式を指定することができるが式の組む合わせによっては無限ループにもなりえるので注意が必要
- while/do..while命令でも記述できるが、変数の値によってループを制御する場合にはよりコンパクトにできる
配列内の要素を順に処理する(for..in命令)
for(仮変数 in 配列/オブジェクト) { ループ内で実行する命令(群) }
- 指定された配列/連想配列やオブジェクト配下の要素/メンバに対して先頭から順番に繰り返し処理を行う
- 仮変数には、配列/連想配列やオブジェクトから取り出された要素のインデックス番号やキー名、メンバ名が格納され、for..inブロックの中で要素値を参照する際に使用することができる
- 仮変数に格納されるのが要素値そのものではないことに注意
- for..inループを利用するのは連想配列、オブジェクトのキーを走査する場合に留め通常配列を走査する場合は原則としてforループを利用するようにする
- for..inループは配列のインデックス番号を取り出すだけなのでコードがあまりシンプルにならない(値そのものではないので、却って誤解を招く)
- 通常配列の内容を取り出す処理はコードによっては正しく動作しない場合がある
ループを途中でスキップ/中断する(break/continue命令)
break;
continue;
- break命令
- 特定の条件を満たした場合にループを強制的に中断したい場合にbreak命令を利用する
- continue命令
- 現在のループだけをスキップして次のループを継続して実行したいという場合はcontinue命令を使用する
- if命令とセットで利用するのが一般的
- ネストされたループの中でbreak/continue命令を使用した場合デフォルトでは最も内側のループを脱出/スキップする
ネストされたループを一気に脱出する(ラベル構文)
ラベル名 :
- ループのスクリプトの先頭にラベルを指定することができる
break ラベル名;
- break/continue命令側では上記のような形式でラベルを指定すると内側のループではなくラベルの付いたループを脱出することができる
例外処理(try..catch..finally命令)
try { 例外が発生するかもしれない命令(群) } catch(例外情報を受け取る変数) { 例外が発生した際に実行する命令(群) } [finally { 例外の有無に関わらず、査収的に実行される命令(群) }]
- 外部要因に依存する処理で完全に防ぐことのできない例外が発生した場合、スクリプト全体が停止しないように例外処理を実装する必要がある
- 例外情報はcatchブロックにErrorオブジェクトとして引き渡される
throw new Error(エラーメッセージ)
- 例外はプログラム中に発生したものを補足するばかりではなく自分で発生させることもできる
- 例外を発生させることを「例外をスローする」ともいう
- throw命令は多くの場合if命令のような条件分岐命令と一緒に使用される
こんなもんかー
【JavaScript】制御命令③
ループを途中でスキップ/中断する(break/continue命令)
while/do..while、for、for..in命令は
あらかじめ決められた終了条件を満たしたタイミングで
ループを終了する
それに対して
「特定の条件を満たした場合にループを強制的に中断したい」
というケースもある
この場合にbreak命令を利用する
var result = 0; for(var i = 0; i <= 100; i++) { result += i; if (result > 1000) { break; } } document.writeln('合計値が1000を超えるのは' + i); //合計値が1000を超えるのは45
変数iを1~100の間で加算していき
合計値(変数result)が1000を超えたところでループを脱出する
このようにbreak命令はif命令と
セットで利用するのが一般的である
ループを完全に中断してしまうのではなく
「現在のループだけをスキップして次のループを継続して実行したい」
という場合はcontinue命令を使用する
以下例!
var result = 0; for(var i = 0; i < 100; i++) { if (i % 2 == 0) { continue; } result += i; }
これは変数iを1~100の間で奇数のみ加算し
その合計値を求めるための記述、ほっほー
カウンタ変数iが偶数(変数iが2で割り切れる)の場合のみ
処理をスキップすることで奇数だけの合計値を求めている
なるほど、なるほどー!
- break命令
- ループ全体を完全に脱出する
- continue命令
- 現在の周回をスキップする
はあく!
ネストされたループを一気に脱出する(ラベル構文)
ネストされたループの中でbreak/continue命令を使用した場合
デフォルトでは最も内側のループを脱出/スキップする
for(var i = 1; i < 10; i++) { for(var j = 1; j < 10; j++) { var k = i * j if (k > 30) { break; } document.write(k + ' '); } document.write('<br />'); }
この例では
変数k(カウンタ変数i、jの積)が
30を超えたところでbreak命令を実行する
これによって内側のループを脱出する
最終的に「積が30以下の値のみを表示する九九表」が
生成される
これを
「一度、積が30を超えたら九九表の出力そのものを停止したい」
としたら
脱出先のループの先頭に
ラベルを指定する必要がある
kuku : for(var i = 1; i < 10; i++) { for(var j = 1; j < 10; j++) { var k = i * j if (k > 30) { break kuku; } document.write(k + ' '); } document.write('<br />'); }
例のようにラベルは
ラベル名 :
という形で指定する
ラベル名は任意の識別子を指定できる
ここで再び識別子の命名規則のおさらいを
- 1文字目は英字/アンダースコア(_)/ドル記号($)のどれか
- 2文字目以降は、1文字目で利用可能な文字か数字
- 変数名に含まれる英数字の小文字/大文字は区別される
- JSで意味を持つ予約語じゃないこと
さすがに覚えた
break ラベル名;
break/continue命令側では
上記のような形式でラベルを指定すると
内側のループではなく
ラベルの付いたループを脱出することができる
うむうむ
switchのcaseブロックと同じような書き方だなーとか
例外処理(try..catch..finally命令)
アプリケーションを実行していると
- 数値を受け取ることを想定した関数に文字列が渡された
- 変数を参照しようとしたら未定義であった
などのプログラミング時には
想定しなかったさまざまなエラー(例外)が
発生するものである
例外の種類によってはプログラミング時に
未然に回避できるものもあるが
- 引数に予期せぬ値が渡された
- 関数やクラスを意図せぬ方法で利用された
など、外部要因に依存する処理では
例外の発生を完全に防ぐことはできない
このような場合にも
スクリプト全体が停止しないようにするのが
例外処理の役割である
例外処理を実現するのは
try..catch..finally命令である
以下構文!
try { 例外が発生するかもしれない命令(群) } catch(例外情報を受け取る変数) { 例外が発生した際に実行する命令(群) } [finally { 例外の有無に関わらず、査収的に実行される命令(群) }]
以下具体例
var i = 1; try { i = i * j; //例外発生 } catch(e) { document.writeln(e.message); } finally { document.writeln('処理は完了しました'); }
j is not defined
処理は完了しました
もしtry..catch..finally命令を利用していない場合
例外(未定義の変数を参照しようとしたため)が発生し
スクリプトが停止してしまうはず、ほう‥
しかし、tryブロックで例外が発生した場合には
処理はそのままcatchブロックに引き継がれ
後続のfinallyブロックも
きちんと処理されていることが確認できる
例外情報はcatchブロックに
Errorオブジェクト(例では変数e)として引き渡される
ここではErrorオブジェクトに用意されている
messageプロパティをつかって
エラーメッセージを表示しているだけだが
その他のブロック同様、必要に応じて
任意の処理を記述することができる
例外はプログラム中に発生したものを
補足するばかりではなく
throw命令を利用し
自分で発生させることもできる
以下構文
throw new Error(エラーメッセージ)
var x = 1; var y = 0; try { if(y == 0){ throw new Error('0で除算しようとしました'); } var z = x / y; } catch(e) { document.writeln(e.message); }
0で除算しようとしました
例外を発生させることを「例外をスローする」ともいう
例では
「変数yが0である場合にErrorオブジェクトを生成し
処理をcatchオブジェクトに移す」
ようにしている
うむうむ
throw命令は多くの場合
if命令のような条件分岐命令と一緒に使用される
改めて自覚しておくべきなのが
例外処理が有効になるのは
スクリプトに対して、外から何らかの値が渡される場合
具体的には実際にそういうスクリプトを書くときに学ぶべし
うむうむー制御命令多くてあたま混乱するな
ちょっとまとめよう
【JavaScript】制御命令②
はい、ちょっと長くなってきたので制御命令も記事を分けるよ!
どんどんいくわよー!
条件式によってループを制御する(while/do..while命令)
JSには繰り返し処理(反復処理)が複数ある
個々の構文はもちろん違いを理解しないといけない、はい!
まず、while/do..while命令
あらかじめ与えられた条件式が
trueである間ループを繰り返す
以下構文
while (条件式) { 条件式がtrueである時に実行される命令(群) }
do { 条件式がtrueである時に実行される命令(群) } while (条件式);
do..while命令のセミコロンを忘れやすいので注意
はい、具体例!
var x = 8; while (x < 10) { document.writeln('xの値は' + x); x++; }
var x = 8; do { document.writeln('xの値は' + x); x++; } while (x < 10);
この処理を実行するとどちらも
xの値は8
xの値は9
と表示される、確かにされました
これだけ見ると同じ処理に見えるけれども
変数xの初期値を10にしてみると
var x = 10; while (x < 10) { document.writeln('xの値は' + x); x++; }
上記は何も返さないのに対し
var x = 10; do { document.writeln('xの値は' + x); x++; } while (x < 10);
こちらは
xの値は10
と表示される
- while命令
- ループの最初で条件式を判定する(前置判定)
- 条件によっては一度もループを実行しない
- do..while命令
- ループも最後で条件式を判定する(後置判定)
- 条件に関わらず、最低1回はループを実行する
この違いは「ループが開始される前から条件式がfalseである場合」に
結果として現れる
ふむ、はあく!
補足:無限ループ
永遠に終了しない(終了条件がtrueにならない)ループのこと
うむ、これはよく聞く言葉なので分かる!
無限ループはブラウザに極端な負担を与え
フリーズさせる原因にもなるため
繰り返し処理を記述する場合
ループが正しく終了するかをあらかじめ確認すること
指定回数だけループを処理する(for命令)
以下構文
for (初期化式; ループ継続条件式; 増減式) { ループ内で実行する命令(群) }
具体例も連続でいくます
for (var x = 8; x < 10; x++){ document.writeln('xの値は' + x); }
実行すると
xの値は8
xの値は9
と表示される
さっきのwhile/do..while命令で使った例を
for命令で書き換えたもの
変数の値によってループを制御する場合には
while/do..while命令で書くよりもコンパクトに記述できる
さっきの構文でもあったようにfor命令は
カッコ内の「初期化式」「ループ継続条件式」「増減式」の3つで
ループを制御する
- forブロックに入った最初のループで初期化式を一回だけ実行する
- 例題では変数xに8をセット
- 一般的にはこの初期化式でfor命令によるループ回数を管理するカウンタ変数を初期化する
- ループ継続条件式はブロック内の処理を継続するための条件式を表す
- 例題では「x < 10」と指定されている、カウンタ変数xが10未満である間だけ繰り返しループする
- ブロック内の処理が1回実行されるたびに増減式が実行される
これらの式には基本的に
任意の式を指定することができるが
式の組む合わせによっては
無限ループにもなりえるので注意が必要
for (var x = 0; x < 5; x--){..} for (;;){..}
一行目のようなforループは
カウンタ変数xの初期値が0で
その後、ループの度にデクリメントされていくので
ループ継続条件である「x < 5」がfalseになることは
永遠にありえない
二行目は初期化式、ループ継続条件式、増減式が
省略されたパターン
この場合for命令は無条件にループを継続する
for命令苦手だ、これは例題たくさんやったほうが
良さげな気がするなーうむむ
配列内の要素を順に処理する(for..in命令)
指定された配列/連想配列やオブジェクト配下の
要素/メンバに対して先頭から順番に繰り返し処理を行う
以下構文
for(仮変数 in 配列/オブジェクト) { ループ内で実行する命令(群) }
仮変数には、配列/連想配列やオブジェクトから取り出された要素の
インデックス番号やキー名、メンバ名が格納され
for..inブロックの中で要素値を
参照する際に使用することができる
仮変数に格納されるのが
要素値そのものではないことに注意
どうしよう何言ってるかわからない←
とりあえず具体例みてみることにする
var member = ['山田', '鈴木', '佐藤']; for (var i in member) { document.writeln('名前は'+ member[i]); } var triangle = {width:30, height:50}; for (var j in triangle) { document.writeln(j + '=' + triangle[j]); } var book = new Object(); book.title = 'JavaScript 本格入門'; book.publish = '技術理論社'; book.price = '3000'; for (var k in book) { document.writeln(k + 'は' + book[k]); }
名前は山田
名前は鈴木
名前は佐藤
width=30
height=50
titleはJavaScript 本格入門
publishは技術理論社
priceは3000
あ、わかったなるほどなるほどふむー
triangleの例の場合仮変数に入っているのは
要素値そのものである30や50じゃなく
要素のキー名であるwidthとかheightなのね
通常配列の内容をfor..inループで
取り出さない方がいいらしい
じゃあなんで例に上げたんだw
- for..inループは配列のインデックス番号を取り出すだけなのでコードがあまりシンプルにならない(値そのものではないので、却って誤解を招く)
- コードによっては正しく動作しない場合がある
らしい、ほう‥
たしかにmemberの例でいくとわざわざ
インデックス番号をとってきているだけだから
仮変数iの中身をあえて表示したりしていないし
する必要もない、よなー
通常配列でほしいのってインデックス番号より
要素値そのものだからこの取り方は無駄が多い
for..inループを利用するのは
連想配列、オブジェクトのキーを走査する場合に留め
通常配列を走査する場合は原則として
forループを利用するようにする
はーい!きをつけます!
長いな制御命令‥記事わけます
【JavaScript】制御命令①
長かった演算子も終わり
お次は制御命令です
プログラムの3つの構造
- 記述された順番に処理を行っていく順次(順接)
- 条件によって処理を分岐する選択
- 特定の処理を繰り返し実行する反復
JSの分岐
JSでは処理を分岐するための命令として
下記の二つが存在する
- if命令
- 2つから片方を選択する(単純分岐)
- switch命令
- 複数の選択から一つを選択する(多岐分岐)
ちょこちょこftlやらでも
やっていたことなので分かると思うが
記述の方法が違うんだろうなー
ふむ、詳細を!
単純分岐を記述する(if命令)
「もし~だったら‥、さもなくば‥」
という構造を表現するためのもの
与えられた条件式の
true/falseという戻り値によって
対応する命令(群)を実行する
下記構文
if (条件式1) { 条件式1がtrueの場合に実行する命令 } [else if (条件式2) { 条件式2がtrueの場合に実行する命令 } [else { 全ての条件式がfalseの場合に実行する命令 }]]
具体例!みてみる!うす!
var x = 15; if (x >= 20) { document.writeln('変数xは20以上!やったね!'); } else if (x >= 10) { document.writeln('変数xは10以上!ひゃほー!'); } else { document.writeln('変数xは10未満‥( ;∀;) '); } //変数xは10以上!ひゃほー!
この例の場合
- 変数xの値が20以上の場合 ⇒ 「変数xは20以上!やったね!」
- 変数xの値が10以上の場合 ⇒ 「変数xは10以上!ひゃほー!」
- 変数Xの値が10未満の場合 ⇒ 「変数xは10未満‥( ;∀;)」
のようなメッセージが表示される
elseブロック、elseifブロックは省略ができる
うむ、こういう概念的なところは
いままで書いてたテンプレートエンジンでも
同じだからわかる
一応メモだが
if命令の場合複数の条件に合致する場合でも
最初のブロックのみ実行される
さっきの具体例でいうと
var x = 30; if (x >= 10) { document.writeln('変数xは10以上!ひゃほー!'); } else if (x >= 20) { document.writeln('変数xは20以上!やったね!'); } else { document.writeln('変数xは10未満‥( ;∀;) '); } //変数xは10以上!ひゃほー!
この形だと最初の条件式で合致してしまったので
elseifブロックは無視されてしまう
条件の順番には気をつけろ、ってことね
あとあとif命令は入れ子にすることで
より複雑な条件分岐を表現することが可能!
さっきのも入れ子にして
var x = 30; if (x >= 10) { if (x >= 20) { document.writeln('変数xは20以上!やったね!'); } else { document.writeln('変数xは10以上!ひゃほー!'); } } else { document.writeln('変数xは10未満‥( ;∀;) '); } //変数xは20以上!やったね!
こんな感じに書いてあげれば
いい感じに動いてくれる、うむー!
制御命令を入れ子にすることを
ネストするといったりもする、うんしってた!
これからやるswitch/for/do..while/whileなどの制御命令でも
ネスト構造にすることができる
コードの可読性の観点から
あまり深いネスト構造にするのは避けるべきだけれど
ネストの深さに応じてインデント(段落)をつけることで
視覚的にコードを読みやすくすることができる
これはなんでも一緒だなー
CSSもHTMLもテンプレートエンジンもなんでも
中カッコは省略可能
ブロックの中の命令が1文の場合に限り
中カッコ({‥})を省略することができる、ほう‥
var x = 15; if (x >= 10) document.writeln('変数xは10以上!ひゃほー!'); else document.writeln('変数xは10未満‥( ;∀;) '); //変数xは10以上!ひゃほー!
だがしかしブロックの範囲が不明確になるため
バグを生みやすくなるので推奨しない、とな
ネストするケースで考えるとわかりやすいとな
var x = 1; var y = 2; if (x == 1) if (y == 1) document.writeln('変数x、yはともに1です'); else document.writeln('変数xは1ではありません'); //変数xは1ではありません
本当は何も返さないのが正しい挙動になるが
elseブロックが意図せず
「x == 1」ではなく「y == 1」に対応しているため
「変数xは1ではありません」が表示される
中カッコを省略した場合、elseブロックは
直近のif命令に対応したとみなされる
ということね、なるほどー!
やっぱりちゃんと中カッコを書いた方がいいね!
var x = 1; var y = 2; if (x == 1) { if (y == 1) { document.writeln('変数x、yはともに1です'); } } else { document.writeln('変数xは1ではありません'); }
うん、まちがいない!
多岐分岐を記述する(switch命令)
はい、以下構文!
switch (式) { case 値1 : 「式 = 値1」である場合に実行される命令(群) [case 値2 : 「式 = 値2」である場合に実行される命令(群) [case 値N : 「式 = 値N」である場合に実行される命令(群) ... [default : 式の値がすべての値に合致しない場合に実行される命令(群)]]] }
switch命令の処理の流れ
- 先頭の式がまず評価される
- 1の値に一致するcaseブロックを実行する
- 一致するcaseブロックが見つからない場合には、最終的にdefaultブロックを呼び出す
defaultブロックは任意だけれど
どのcaseブロックにも一致しなかった場合の挙動を
明確にするためにも省略しないことを推奨する
ほっほーう
はい!具体例!
var rank = 'B'; switch (rank) { case 'A' : document.writeln('Aランク!さすがっす!!'); break; case 'B' : document.writeln('Bランク!がんばりました!'); break; case 'C' : document.writeln('Cランク!まだまだいけます!'); break; default : document.writeln('ランク外‥がんばりましょう!'); } //Bランク!がんばりました!
それぞれのcaseブロックの末尾にbreak命令をして
現在のブロックから処理を脱出している
switch命令は合致するcaseブロックに処理を移動するだけで
caseブロックを終了したところで
自動的にswitchブロックから脱出するしくみは備えていない
なんでそうしたのこれw
break命令を指定していない場合
次のcaseブロックが続けて実行されてしまうので
意図した結果が得られないので注意
えじゃあなんでdefaultブロックには
break命令がないの?ってなったので
defaultブロックの位置を変えて確認してみますた
結論、最後だからってだけなのね
閉じカッコ前はbreak命令なくても
続けて実行する処理が存在しないからなくても可
defaultブロックが他のcaseの間にあった場合
defaultでもbreak命令は必要、うむ
意図的にbreak命令を略記して
複数ブロックを続けて実行する(フォールスルー)という書き方もできるが
コードの流れがわかりにくくなる原因となるので
通常は避けるべきだそうな、そうかー
ただし例外として以下のようなケースでは
複数ブロックをbreak命令をはさまずに続けて
記述しても可
var rank = 'B'; switch (rank) { case 'A' : case 'B' : case 'C' : document.writeln('合格!(・∀・)イイ!!'); break; case 'D' : document.writeln('不合格!(´;ω;`)'); break; } //合格!(・∀・)イイ!!
この例では変数rankがA、B、Cのとき
「合格!(・∀・)イイ!!」というメッセージを
Dの場合「不合格!(´;ω;`)」というメッセージを
それぞれ表示するます、ふむー
【JavaScript】演算子②
はーい引き続き演算子やります!
比較演算子
左辺と右辺を比較してその結果をtrue/falseとして返す
めっちゃftlやvmで利用していたので
これもある程度ならわかる、はず!
- ==
- 左辺と右辺の値が等しい場合はtrue
- 例>> 5 == 5 //true
- !=
- 左辺と右辺の値が等しくない場合はtrue
- 例>> 5 != 5 //false
-
- 左辺が右辺より小さい場合はtrue
- 例>> 5 < 5 //false
- <=
- 左辺が右辺より以下の場合はtrue
- 例>> 5 <= 5 //true
- >
- 左辺が右辺より大きい場合はtrue
- 例>> 5 > 3 //true
- >=
- 左辺が右辺より以上の場合はtrue
- 例>> 5 >= 3 //true
- ===
- 左辺と右辺の値が等しくてデータ型も同じ場合はtrue
- 例>> 5 === 5 //true
- !==
- 左辺と右辺の値等しくない場合、またはデータ型が異なる場合はfalse
- 例>> 5 !== 5 //false
- ?:
- 「条件式?式1:式2」条件式がtrueの場合は式1を、falseの場合は式2を返す
- 例>> (x == 1) ? 1:0 //
ふむ
等価演算子(==)
オペランドのデータ型によって
比較の基準が異なるので注意
左辺/右辺の型が同じ
- 文字列/数値/論理型
- 単純に双方が等しいかを判定
- 配列/オブジェクト
- 参照先が等しいかを判定
- null/undefined
- 双方ともnull/undefined、またはnullとundefinedの比較はすべてtrue
左辺/右辺の型が異なる
- 文字列/数値/論理型
- 文字列/倫理型を数値に変換したうえで判定
- オブジェクト
- 基本型に変換したうえで判定
要は
データ型が違う
データ型をなんとか変換して等しいとみなせないか頑張る
ってことですね
document.write(1 == true); //true
1は真偽でいうと真なので
結果はtrue
うむ、わかりますた
またここで注意
var ary1 = [0, 1, 2]; var ary2 = [0, 1, 2]; document.writeln(ary1 == ary2); //false
参照型の場合
変数の中には参照値(メモリ上のアドレス)が格納される
ということだったので
アドレスが一致した場合のみtrueを返す
上記の場合、実際の配列の中身は同じだけれど
あくまで参照値のary1とary2が同じでないので
falseが返ってくるわけですね、わかります
等価演算子(==)と同値演算子(===)
等価演算子はオペランドが
「なんとか等しいとみなせないか」とJS側で
あれこれ頑張ってくれる演算子
だがしかし、これが大きなお世話な時もある
document.writeln('3.14E2' == 314); //true 指数表現 document.writeln('0x10' == 16); //true 16進数表現 document.writeln('1' == 1); //true
これは余計なお世話ですねー
そんな時に利用するのが同値演算子(===)
document.writeln('3.14E2' === 314); //false document.writeln('0x10' === 16); //false document.writeln('1' === 1); //false
うむ!文字列型と数値型だから
すべてfalse!「'1'」も文字列扱い!
わかりやすい
論理演算子
複数の条件式または論理値を
論理的に結合し、その結果を真偽で返す
これはめっちゃつかってたのでわかる!
- &&(AND)
- 左右の式がともにtrueの場合はtrue
- 例>>100 == 100 && 1000 == 1000 //true
- ||(OR)
- 左右の式のどちらかがtrueの場合はtrue
- 例>>100 == 100 || 1000 == 500 //true
- !(NOT)
- 式がfalseの場合はtrue
- 例>>!(1000 == 500) //true
var x = 1; var y = 2; document.writeln(x == 1 && y == 1); //false document.writeln(x == 1 || y == 1); //true
そりゃそうだ、うむ、ここは基本的にわかってる
ここだけ注意
論理演算子のオペランドは
必ずしも論理型のtrue/falseである必要はない
- false(偽)
‐-空文字(' ')、数値の0、NaN(Not a Number)、null、underdefinedなどの値
- true(真)
- それ以外の値
うむ、覚えよう
論理積演算子(&&)と論理和演算子(||)
論理積/論理和演算子を利用する場合の注意!
左辺だけが評価されて右辺が評価されない
ことがある
ふむふむそうだね、あるね
具体例をチェック!
if(x == 1){document.writeln('こんにちは');} x == 1 && document.writeln('こんにちは');
これは意味的には等価になる
1行目は変数xが1である場合に「こんにちは」と表示する
というスクリプトだがxが1でないので
それ以降の処理は実行されない
2行目は左辺がfalseな時点で
条件式全体は必ずfalseになるため
右辺は評価(実行)されない
どちらも変数x==1でないのでdocument.writeln();は
実行されない、わかります
こういう演算のことをショートカット演算とか
短絡演算という、うむ
あ、2行目みたいな記述はやめたほうがいいらしい
- 条件分岐であることが一見してわかりにくい
- 右辺が実行されるか曖昧になるため、思わぬバグになりやすい
からとか!ふむー
原則として
論理演算子の後方には、関数の呼び出し
インクリメント/デクリメント演算子、代入演算子など
値を実際に操作するような式を含めるべきではない
うむうむ
ビット演算子
整数値を2進数で表した場合の各桁に対して(ビット単位に)
論理計算を行う演算子のこと
なにそれおいしいの?って感じです←
読み飛ばしても構わないって書いてあるから
例は飛ばしてどんなのが使えるかだけメモ
軽く読むだけしておく
代入演算子でちょこっと調べたりしたし!
- &
- 左式と右式の両方にセットされているビット
- |
- 左式と右式のどちらかにセットされているビット
- ^
- 左式と右式のどちらかにセットされていて、かつ、両方にセットされていないビット
- ~
- ビットを反転
- <<
- ビットを左にシフト
- 例>> x = 10; x >>= 1 //5
- >>
- ビットを右にシフト
- >>>
- ビットを右にシフト、かつ、左端を0で埋める
その他の演算子
今まで紹介したカテゴリに分類できない演算子
- ,(カンマ)
- 左右の式を続けて実行
- delete
- オブジェクトのプロパティや配列の要素を削除
- instanceof
- オブジェクトが指定されたクラスのインスタンスかを判定
- new
- 新しいインスタンスを作成
- typeof
- オペランドのデータ型を取得
- void
- 未定義値を返す
delete演算子
オペランドに指定した変数や
配列要素、オブジェクトのプロパティを破棄する
delete演算子は削除に成功した場合trueを
失敗した場合はfalseを返す
はい、具体例!
var ary = ['赤', '青', '黄']; document.writeln(delete ary[0]); //true document.writeln(ary); //,青,黄 ① var obj = {x:1, y:2}; document.writeln(delete obj.x); //true document.writeln(obj.x); //undefined var obj2 = {x:obj, y:2}; document.writeln(delete obj2.x); //true document.writeln(obj); //[object Object] ② var data1 = 1; document.writeln(delete data1); //false document.writeln(data1); //1 ③ data2 = 10; document.writeln(delete data2); //true document.writeln(data2); //エラー(data2は存在しない)
- 配列要素を削除した場合、該当する要素が削除されるだけで後ろの要素が繰り上がるわけではない(インデックス番号は変わらない)①
- プロパティを削除した場合も、プロパティそのものが削除されるだけで、プロパティが参照するオブジェクトが削除されるわけではない②
- 明示的に宣言された変数を削除することはできない③
また、組み込みオブジェクトや
クライアントサイドJavaScript標準のオブジェクトに含まれるメンバの中には
delete演算子で削除できないプロパティもある、とか
クライアントサイドJavaScript標準のオブジェクトに関しては
後述があるようなので一旦スルー
typeof演算子
オペランドに指定した変数/リテラルの
データ型を表す文字列を返す演算子
おお、なんか意味わかる
var num = 1; document.writeln(typeof num); //number var str = 'こんにちは'; document.writeln(typeof str); //string var flag = true; document.writeln(typeof flag); //boolean var ary = ['赤', '青', '黄']; document.writeln(typeof ary); //object var ary2 = {x:1, y:2}; document.writeln(typeof ary2); //object
配列やオブジェクトは
一律「object」と返される点に注意
また、ほかのデータ型でも
ラッパーオブジェクトとして宣言されている場合は
「object」とみなされる
ラッパーオブジェクトに関しては後述があるみたい
演算子の優先順位と結合則
優先順位
一つの式の中に複数の演算子が含まれる場合
優先順位の高いほうから演算する、ほう
優先度高いほうから‥
- 配列([])、かっこ(())
- インクリメント(++)、デクリメント(--)、減算(-)、反転(~)、否定(!)
- 乗算(*)、除算(/)、剰余(%)
- 加算(+)、減算(-)、文字列連結(+)
- シフト(<<、>>、<<<)
- 比較(<、<=、>=、>)
- 等価(==)、不等価(!=)、同値(===)、非同値(!==)
- AND(&)
- OR(|)
- 論理積(&&)
- 論理和(||)
- 条件(?:)
- 代入(=)、複合代入(+=、-=など)
- カンマ(,)
この優先順位も大事だけど
丸かっこで演算の優先順位を明示したほうがいい、そうですね間違いない
【JavaScript】演算子①
引き続き!
今回は演算子
これはftlとかvmとかでちょいちょい使っていたから
ちょっとはわかると思うのだが‥
演算子の基本
一応メモ
算術演算子
代数演算子ともいう、ほう、こっちはきいたことない
標準的な四則演算をはじめとして、数学的な演算を行う
- +
- 数値の加算
- 例>> 3 + 5 //8
- -
- 数値の減算
- 例>> 10 - 7 //3
- *
- 数値の乗算
- 例>> 3 * 5 //15
- /
- 数値の除算
- 例>> 10 / 5 //2
- %
- 数値の剰余
- 例>> 10 % 4 //2
- ++
- 前置加算
- 例>> x = 3; a = x++; //aは3
- ++
- 後置加算
- 例>> x = 3; a = ++x; //aは4
- --
- 前置減算
- 例>> x = 3; a = x--; //aは3
- --
- 後置減算
- 例>> x = 3; a = --x; //aは2
ふむ、なんとなくそれぞれわかります
注意すべき点もチェック
加算演算子
document.writeln(10 + 1); //11 document.writeln('10' + 1); //101 var today = new Date(); document.writeln(1234 + today); //1234Sun Jan 15 10:34:33 UTC+0900 2012
オペランドが両方数値⇒問題なく加算
片方が文字列⇒文字列連結演算子になる
オペランドの片方がオブジェクト⇒オブジェクトの要素の形式によって処理を行う
うむ、例だと文字列形式になるから
文字列連結演算子扱いなのね、なるほどなるほど
インクリメント演算子(++)とデクリメント演算子(--)
オペランドに対して1を加算/減算する‥
x++はx+1と一緒、ふむ、そうですね
ただしほかの変数に代入する場合は注意、ほう
var x = 3; var y = x++; document.writeln(x); //4 document.writeln(y); //3 var x = 3; var y = ++x; document.writeln(x); //4 document.writeln(y); //4
インクリメント処理が先か代入が先かっていうことね、ふむ
少数点を含む計算には要注意
document.writeln(0.2 * 3); //0.6000000000000001
JSが数値演算を2進数で行うための誤差がでる、ほう‥
document.writeln(0.2 * 3 === 0.6); //false
小数点を含む場合の演算や
値の比較を行う際は下記に注意
- 値を一旦整数にしてから演算する
- 1の結果を再び小数点数に戻す
document.writeln(((0.2 * 10) * 3) / 10); //0.6 document.writeln((0.2 * 10) * 3 === 0.6 * 10); //true
値を何倍にするかは有効桁数によって決めるそうな‥ほう‥
で、なんなの有効桁数ってw
0ではない数字に挟まれた0は有効である
0ではない数字より前に0がある場合、その0は有効ではない
小数点より右にある0は有効である
- 39008 は有効数字5桁である
- 0.012 は有効数字2桁である
- 35.00 は有効数字4桁である
なるほどWikipediaさんありがとう
で、話を戻すと
"値を何倍にするかは有効桁数によって決める"というのは
0.2351という小数点数で小数点以下2桁まで保証したいなら
- 100倍して23.51としたものを演算する
- 最終的な結果を小数点以下で四捨五入する
- 2の結果を再び小数点数に戻す
という形で処理をするべし
これはなんでなんだろ‥明日きいてみる
代入演算子
- =
- 変数などに値を代入する
- 例>> x = 1 //1
- +=
- 左辺の値に右辺の値を加算したものを代入する
- 例>> x = 3; x += 2 //5
- -=
- 左辺の値から右辺の値を減算したものを代入する
- 例>> x = 3; x -= 2 //1
- *=
- 左辺の値に右辺の値を乗算したものを代入する
- 例>> x = 3; x *= 2 //6
- /=
- 左辺の値を右辺の値を除算したものを代入する
- 例>> x = 3; x /= 2 //1.5
- %=
- 左辺の値を右辺の値を除算した余りを代入する
- 例>> x = 3; x %= 2 //1
- &=
- 左辺の値を右辺の値で論理積演算した結果を代入する
- 例>> x = 10; x &= 5 //0
- |=
- 左辺の値を右辺の値で論理和演算した結果を代入する
- 例>> x = 10; x |= 5 //15
- ^=
- 左辺の値を右辺の値で排他的論理和演算した結果を代入する
- 例>> x = 10; x ^= 5 //15
- <<=
- 左辺の値を右辺の値だけ左シフトした結果を代入する
- 例>> x = 10; x <<= 1 //20
- >>=
- 左辺の値を右辺の値だけ右シフトした結果を代入する
- 例>> x = 10; x >>= 1 //5
- >>>=
- 左辺の値を右辺の値だけ右シフトした結果を代入する
- 例>> x = 10; x >>>= 1 //5
&=以降のビット演算子と連動した辺りが
バカな私には難しい
ていうかビット演算子がわかりにくい
検索でなんとか解決
x ●= y; //これと x = x ● y; //これは同じこと
なーるほどね!
特に変数自身に対する演算の結果を
もとの変数に書き戻したい場合に
代入演算子を利用すれば
式をシンプルに記述できる、ほっほーう!はあく!
基本型と参照型による代入の違い
おおきたこれ!
今日昼に説明受けたところですね
var x = 1; var y = x; x = 2; document.writeln(y); //1
まず基本型の場合
直観的にわかる
変数xをyに引き渡す場合、その値がコピーされる
うん、そうだと思った!
これ、値渡しというそうな
既にyに値をコピーしているから元のxをあとから変更しても
既に値を代入済みのyには影響がない
それでは次、参照型だとどうなるか
var ary1 = [0, 1, 2]; var ary2 = ary1; ary1[0] = 5; document.writeln(ary2); //5, 1, 2
1行目で配列リテラルを変数ary1にセットして
2行目でそれをary2に代入している
3行目でary1の要素を変更した結果
ary2の数値も変更されている
えっとどういうことかというと
値そのものを格納したのでなく
値が格納されているアドレスだけを変数に格納している
あくまでary2に入っているのはary1に格納されているアドレスだけ
こういうのを参照渡しというらしい
説明受けてたからめっちゃわかる!うれしいw
記事が長くなってきたので
比較演算子からは次に
【JavaScript】データ型
基本型と参照型の2つに分かれる
JSで扱うことができる主なデータ型
基本型
- 基本型の変数には値そのものが直接格納される
- 数値型(number)
- 文字列型(string)
- 真偽型(boolean)
- 特殊型(null/undefined)
参照型
- 参照値(値を実際に格納しているメモリ上のアドレス)を格納する
- 配列(array)
- オブジェクト(object)
- 関数(function)
うむー‥なんとなくわかる
リテラル
データ型に格納できる値そのものをリテラルというらしい
の3つがある、ふむー
数値リテラル(number)
浮動小数点ってなんぞや
検索かけてみたけどわからない‥
Wikipedia読んでもわからなかったので
とりあえずあとで聞いてみることにして先に進んでしまうのよ
文字列リテラル(string)
文字列リテラルはシングルクォート(')、ダブルクォート(")で囲む必要あり
'ここが文字列リテラルだよ' "ここもだよ"
文字列リテラルでは
エスケープシーケンスしなければならい文字列があるそうな
そもそもエスケープシーケンスとは‥
特別な意味を持つ文字を「\+文字」という形式で
表現すること、ふむ
主なエスケープシーケンスめもめも
- \b
- バックスペース
- \f
- 改ページ
- \n
- 改行(LF:Line Feed)
- \r
- 復帰(CR:Carriage Return)
- \t
- タブ文字
- \\
- 円マーク
- \'
- シングルクォート
- \"
- ダブルクォート
- \xXX
- Latin-1文字(XXは16進数)
- \uXXXX
- Unicode文字(XXXXは16進数)
たとえばたとえば
document.write('He\'s Hero!!');
とか
window.alert('このあと改行が入ります。\r\nほら、入った!');
とか
さてここで一個疑問が
なんで\r、\n両方入れる必要があるのよ
そもそも\rの復帰(CR:Carriage Return)ってなんなのよ
調べてみた‥
カーソルを同一行の先頭位置に移動させる意味を持つ、ふむー
いや意味わかってもなんで両方必要なのかわからず‥
とりあえずタイプライターが由来なのはわかった
ためしにどんな風に表示されるか下記で試してみた
document.write('このあと改行が入ります。\r\nほら、はいった!'); window.alert('このあと改行が入ります。\r\nほら、はいった!');
document.write();ではどっちかだけでも改行され
window.alert();では改行されないという結果に
なんでだー
とりあえず両方書かなきゃダメなのはわかったけれども
はい!次!
配列リテラル(array)
うん、配列はわかる!
配列リテラルはカンマ区切りの値を
ブラケット([‥])で括った形式で表現する
['りんご', 'みかん', 'なし']
配列内の値にはインデックス番号でアクセスできる、うむ
インデックスは0スタートなのをお忘れなく
これ私バカだからなんかうっかりしがちなのよなー
var ary = ['りんご', 'みかん', 'なし']; document.writeln(ary[0]); //1番目の要素を取得(りんご)
配列の入れ子も可能
var ary = ['りんご', ['みかん','いよかん'], 'なし']; document.writeln(ary[1][0]); //2番目の要素の中の1番目の要素を取得(みかん)
インデックス番号を繰り返すことで
入れ子の入れ子のそのまた入れ子の‥って
個々の要素にアクセスできるのね、ふむ
オブジェクトリテラル(object)
名前をキーにアクセスできる配列です
‥ってどういうこと?ってなったけど
要はさっきの配列のインデックス番号が
文字列になりましたって話なのね、はあく
ハッシュ、連想配列ともいうらしい、ふむー
他の言語ではオブジェクトと連想配列は
イコールじゃないらしい
Javaだとどーなんだろー‥あとできいてみよ
配列内の個々のデータは要素と呼んでいるけれど
オブジェクト内のデータはプロパティと呼ぶらしい、なんで
文字列、数値などの情報に限らず
関数も含むことができるとか
ほんでもって関数を格納したプロパティのことを
メソッドという、ほう‥なんかよく聞くぞこの単語
またあとで詳しく説明があるとのことなので
ここでは一旦スルーして、オブジェクトオブジェクト‥
var obj = {x:1, y:2, z:3}; document.writeln(obj.x); //xの中身(1) document.writeln(obj['x']); //xの中身(1)
オブジェクトリテラルは大カッコじゃないんだね!
中カッコなんだね!コロン(:)でつなぐ、うむ
個々のプロパティにアクセスするには
ドット演算子、ブラケット構文の2つの方法がある、ほう
obj.x //ドット演算子 obj['x'] //ブラケット構文
ドット演算子のプロパティ名は識別子
ブラケット構文のプロパティ名は文字列
ここで識別子の命名規則のおさらいをw
- 1文字目は英字/アンダースコア(_)/ドル記号($)のどれか
- 2文字目以降は、1文字目で利用可能な文字か数字
- 変数名に含まれる英数字の小文字/大文字は区別される
- JSで意味を持つ予約語じゃないこと
ふむ、たとえばプロパティ名が数字だった場合は
ドット演算子でアクセスすることができない、ってことね、はあく
関数リテラル(function)
関数!きた!
関数とは‥
何かしらの入力値(引数)を与えることによって
あらかじめ決められた処理を行い
その結果(戻り値)を返すしくみ
ふむ
これも後述すると‥ほう‥了解です‥
未定義値(undefined)
変数の値が定義されていないことを表す
var x; var obj = {a:1}; document.writeln(x); //undefined(値が設定されていない) document.writeln(obj.x); //undefined(プロパティが存在しない)
nullはあまり利用しないそうなのでまたあとで
よし!データ型!おわり!
次は演算子!!!