Navigate back to the homepage

JavaScript のお勉強 with MDN web docs

Kenya Hondoh
August 16th, 2020 · 1 min read

概要

  • JavaScript を雰囲気で書いていたので,MDN Web Docs の JavaScript 入門編を読むことにした。
  • 「へ〜」と思ったことを書き留める。

JavaScript におけるセミコロンの扱い

  • 1行1式ならセミコロン不要。
  • 複数行を1行にまとめて書くなら式の区切りとして必要。
  • でも,バグの元になるので基本的につけることを推奨。
1// 複数の式を1行で書く時セミコロンいる
2const a = 1; const b = 2; const c = 3;
3// 1行1式ならつけなくてもOK
4console.log( a + b + c )

巻き上げ

宣言してない変数を利用したとき,普通は ReferenceError という例外が発生する。

1// var で宣言する場合
2var declared_var;
3declared_var = '宣言済みの変数';
4
5// 宣言してない場合
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 で宣言した変数についても「巻き上げ」が起こることになった。

だそうです。なお,constlet で後出し宣言した場合は,巻き上げは起こらず,ちゃんと例外が発生する。以下に例を示す。

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); // 1111
3console.log(+a + +a); // 22

おそらく JS 特有でかつ知ってたら便利そうなのでメモしておく。

letconst のスコープ

ECMAScript 2015 からは、letconst による変数宣言はブロックスコープとなる。

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); // ReferenceError
6 console.error(err.message); // assignment to undeclared variable 初期化していない変数
7}

これを使ってカスタムな例外を発生させる:

1function 例外発生器() {
2 const 文字列型変数 = 1000;
3 if (typeof(文字列型変数) == 'number') {
4 throw (new Error('これは文字列型ではないよ'));
5 }
6 }
7
8 try {
9 例外発生器();
10 } catch (err) {
11 console.error(err.name); // Error
12 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 ];
9
10 const 棚のサイズ = 8;
11 let 棚の中身 = [];
12
13 野菜ループ:
14 for (let 野菜 of 野菜たち) {
15 // 棚に詰める野菜
16 console.log(野菜.name);
17 console.log(野菜.stock);
18
19 棚に詰める:
20 for (let i = 0; i<野菜.stock; i++) {
21 棚の中身.push(野菜.name)
22 if (棚の中身.length == 棚のサイズ) {
23 break 野菜ループ;
24 }
25 }
26 }

for...infor...of

  • for...in では,プロパティ名に対してループを実行する。
  • for...of では,プロパティの値に対してループを実行する。
1const arr = [3, 5, 7];
2arr.foo = 'hello';
3
4for (let i in arr) {
5 console.log(i); // "0", "1", "2", "foo" が出力される
6}
7
8for (let i of arr) {
9 console.log(i); // 3, 5, 7 が出力される
10}

関数の挙動

⚠️ 関数にオブジェクトを渡し,関数内でオブジェクトのプロパティを変更すると その変更はグローバルな値に変更を与える。注意。

1function modifyObj(obj) {
2 obj.name = '田中太郎';
3}
4
5var obj = {name: '田中花子'};
6console.log(obj.name); // 田中花子
7
8modifyObj(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); // 100
5}

再帰関数

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}

More articles from Kenya Hondoh

レガシーコードからの脱却 - Beyond Legacy Code

読中メモ

July 5th, 2020 · 6 min read

Speakerdeck のスライドを Markdown や HTML に埋め込む。

MDX に埋め込むためのコンポーネントをつくる。

June 2nd, 2020 · 1 min read
© 2020–2023 Kenya Hondoh
Link to $https://twitter.com/EarllibraryLink to $https://github.com/kenchonLink to $https://www.linkedin.com/in/kenya-hondoh-2a7067123/