概要
- JavaScript を雰囲気で書いていたので,MDN Web Docs の JavaScript 入門編を読むことにした。
- 「へ〜」と思ったことを書き留める。
JavaScript におけるセミコロンの扱い
- 1行1式ならセミコロン不要。
- 複数行を1行にまとめて書くなら式の区切りとして必要。
- でも,バグの元になるので基本的につけることを推奨。
1// 複数の式を1行で書く時セミコロンいる2const a = 1; const b = 2; const c = 3;3// 1行1式ならつけなくてもOK4console.log( a + b + c )
巻き上げ
宣言してない変数を利用したとき,普通は ReferenceError
という例外が発生する。
1// var で宣言する場合2var declared_var;3declared_var = '宣言済みの変数';45// 宣言してない場合6undeclared_var = '未宣言の変数'; // ReferenceError になる
でも,次の場合は例外を発生しない。
1console.log(undeclared_var === undefined); // true 例外を発生しない。2var undeclared_var;
↑のように,あとから undeclared_var
を宣言すると undeclared_var
には undefined
が入る。
これは,上記のコードが以下のように解釈されるかららしい。
1var undeclared_var;2console.log(undeclared_var === undefined); // true 例外を発生しない。
このように,後置的に宣言した変数があたかもソースコードの頭で宣言したように見える仕様を 巻き上げ という。
なんのためにこんな仕様があるのか?
📚 NOTE: なんのためにこんな仕様があるのか
かつての JS は,関数を使う時はあらかじめ関数を宣言する必要があった。 でも,関数をコードの下の方で宣言したいお気持ちもあった。 それを可能にしたところ,その副作用として
var
で宣言した変数についても「巻き上げ」が起こることになった。
だそうです。なお,const
,let
で後出し宣言した場合は,巻き上げは起こらず,ちゃんと例外が発生する。以下に例を示す。
1console.log(undeclared_var === undefined); // ReferenceError になる2const undeclared_var;
変数名に Unicode 文字列が使える
例)
1const 俺の名前 = '田中太郎';2console.log(俺の名前);
const
で宣言されたオブジェクトの挙動
const
で宣言されたとしても,オブジェクトのプロパティは保護されない。
以下の例のように,プロパティは書き換えられる:
1const MY_ARRAY = ['HTML','CSS'];2MY_ARRAY.push('JAVASCRIPT');3console.log(MY_ARRAY); //logs ['HTML','CSS','JAVASCRIPT'];
数値から文字列への変換
数値を表す文字列(例:"11"
)の先頭に +
をつけると数値として扱われる。
1const a = '11';2console.log(a + a); // 11113console.log(+a + +a); // 22
おそらく JS 特有でかつ知ってたら便利そうなのでメモしておく。
let
と const
のスコープ
ECMAScript 2015 からは、let
や const
による変数宣言はブロックスコープとなる。
1var global = 'トップレベルのグローバル変数';2let local = 'トップレベルのローカル変数';3{4 var global = 'ブロック内で宣言したグローバル変数';5 let local = 'ブロック内で宣言したローカル変数';6}7console.log(global); // ブロック内で宣言したグローバル変数8console.log(local); // トップレベルのローカル変数
false
をとりうる値
以下の値は false
と評価される (Falsy な値と呼ばれている)。
false
undefined
null
0
NaN
- 空の文字列 (
""
)
なお,これ以外の値はすべて true
になる。
例外
例外,こんな感じで書ける。
1try {2 初期化していない変数 = '例外発生するぞ';3 }4 catch (err) {5 console.error('例外発生'); // console.log() よりも推奨される6 }
try
ブロック では,任意の 型の例外を発生(throw
)し,catch
ブロックに渡すことができる。
try...catch...finally
finally
ブロックは,例外がcatch
されなくても実行される。finally
があることで,例外発生時にスクリプトを停止させることができる。例えば,スクリプトで使用していたリソースを解放しなければならないとき。
例)
1openMyFile();2try {3 writeMyFile(theData); // ここでエラーがスローされる可能性がある4} catch(e) {5 handleError(e); // エラーを受け取り、それを処理する6} finally {7 closeMyFile(); // 常にリソースが閉じられる8}
例外でエラーキャッチするとき
err.name
, err.message
を使って,エラーのプロパティにアクセスする。
1try {2 初期化していない変数 = '例外発生するぞ';3}4catch (err) {5 console.error(err.name); // ReferenceError6 console.error(err.message); // assignment to undeclared variable 初期化していない変数7}
これを使ってカスタムな例外を発生させる:
1function 例外発生器() {2 const 文字列型変数 = 1000;3 if (typeof(文字列型変数) == 'number') {4 throw (new Error('これは文字列型ではないよ'));5 }6 }78 try {9 例外発生器();10 } catch (err) {11 console.error(err.name); // Error12 console.error(err.message); // これは文字列型ではないよ13 }
ラベル付き文
ループに対してラベルをつけることができる。
1// ループに名前をつけられる2 楽しいループ:3 for (let word of ['JS楽しい!', 'JS is fun!']){4 console.log(word);5 }
こうすることで,入れ子になったループでどのループを break
するか決めることができる。
例)
1// 野菜を棚に詰めていく。2 // 棚が満杯になったら終了する。3 const 野菜たち = [4 {name:'にんじん', stock:3},5 {name:'たまねぎ', stock:1},6 {name:'みょうが', stock:4},7 {name:'れんこん', stock:1},8 ];910 const 棚のサイズ = 8;11 let 棚の中身 = [];1213 野菜ループ:14 for (let 野菜 of 野菜たち) {15 // 棚に詰める野菜16 console.log(野菜.name);17 console.log(野菜.stock);1819 棚に詰める:20 for (let i = 0; i<野菜.stock; i++) {21 棚の中身.push(野菜.name)22 if (棚の中身.length == 棚のサイズ) {23 break 野菜ループ;24 }25 }26 }
for...in
と for...of
for...in
では,プロパティ名に対してループを実行する。for...of
では,プロパティの値に対してループを実行する。
1const arr = [3, 5, 7];2arr.foo = 'hello';34for (let i in arr) {5 console.log(i); // "0", "1", "2", "foo" が出力される6}78for (let i of arr) {9 console.log(i); // 3, 5, 7 が出力される10}
関数の挙動
⚠️ 関数にオブジェクトを渡し,関数内でオブジェクトのプロパティを変更すると その変更はグローバルな値に変更を与える。注意。
1function modifyObj(obj) {2 obj.name = '田中太郎';3}45var obj = {name: '田中花子'};6console.log(obj.name); // 田中花子78modifyObj(obj);9console.log(obj.name); // 田中太郎
関数式
関数に名前をつけて,関数内で自身を参照できる。階乗の計算は以下のようにシンプルにかける:
1var factorial = function fac(n) {2 return n<2 ? 1 : n*fac(n-1)3};4console.log(factorial(5)); // 120
条件によって関数を定義したりできる:
1var func;2if (type == 'max') {3 func = function(array) {4 return array.max;5 }6}
関数のスコープ
関数の内部で宣言された変数は外部からアクセスできない。
1function scope() {2 var unaccessible = 100;3}4console.log(unaccessible); // ReferenceError
一方,関数が定義されたスコープ内の変数には,その関数からアクセスすることができる。
1var accessible = 100;2scope();3function scope() {4 console.log(accessible); // 1005}
再帰関数
DOM ツリーの全てのノードを取得するのに再帰を使うと簡単にできる:
1function walkTree(node) {2 if (node == null) //3 return;4 // ノードに対し処理を行う5 for (var i = 0; i < node.childNodes.length; i++) {6 walkTree(node.childNodes[i]);7 }8}