#はじめに
改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで を読んで感じたことや知らなかったこと、覚えておきたいこと等を随時メモしています。自分用。
#Chapter 1|イントロダクション
RIA技術 … RIA【Rich Internet Applications】Flash等のこと
インタプリタ型言語 … 【INTERPRETER】プログラムを先頭方逐一解析し、コンピューターに理解できる形式に翻訳しながら実行していく言語
コンパイラ型言語 … 【COMPILER】コンパイラ(一括翻訳)が必要
コンパイラ型 | インタプリタ型 | |
---|---|---|
プログラム実行 | コンパイル作業が必要 | ソースファイルを直接実行 |
処理速度 | 速い | 遅い |
言語の例 | C, C++ | Perl, PHP |
ES6のブラウザ対応状況
ECMAScript 6 compatibility table
https://kangax.github.io/compat-table/es6/
▼検証ツールのアイコン
(F10) … ステップオーバー(1行単位に実行。ただし、関数があった場合もこれを実行して次の行へ)
(F11) … ステップイン(1行単位に実行)
(Shift + F11) … ステップアウト(現在の関数が呼び出し元に戻るまで実行)
#Chapter 2|基本的な書き方を身につける
コメントイン … コメントアウトの反対。コメントを外して再びコードを有効にすること。
・変数に初期値を与えなかった場合、デフォルトで「undefined」が割り当てられる。
・変数の宣言は省略することができるが、原則として避けるべきである。
ES2015
let … 変数の重複禁止、ブロックスコープの認識が可能
命名規則
・定数は大文字とアンダースコアを用いる
・JavaScriptの予約語を命名に使用しない
▼JavaScriptの予約語
break, case, catch, class, const, continue, debugger, default, delete, do, else, export, extends, finally, for, function, if, import, implements, in, instanceof, interface, new, package, private, protected, public, return, super, switch, this, throw, try, typeof, var, void, while, with, yield
マジックナンバー…コンピュータプログラムのソースコードなどに直に記述された数値で、その意味や意図が記述した本人以外には自明ではないもの。「この数字の意味はわからないが、とにかくプログラムは正しく動く。まるで魔法の数字だ」という皮肉を含む。
JavaやC#はデータ型が厳密。数値を格納するために用意された変数に文字列を格納することは許されない。JavaScriptは柔軟。
■データ型
分類 | データ型 | 概要 |
---|---|---|
基本型 | 数値型(number) | 数値 |
文字列型(string) | 文字列 | |
真偽型(boolean) | true(真)/false(偽) | |
シンボル型(symbol) | シンボル ES2015
|
|
特殊型(null/undefined) | 空/未定義 | |
参照型 | 配列(array) | データの集合(各要素にはインデックス番号でアクセス可能) |
オブジェクト(object) | データの集合(各要素には名前でアクセス可能) | |
関数(function) | 関数 |
基本型 … 値そのものが変数に格納される
参照型 … 参照値(値を実際に格納しているメモリ上のアドレス)が格納される
■リテラル
リテラル … 【literal】ソースコードにべた書きした文字列や数字のこと
◆数値リテラル
※指数表現 3.14e5 = 3.14 × 105 = 3.14 × 100000 = 314000
◆文字列リテラル
エスケープシーケンス … エスケープ処理が必要な特別な意味を持つ文字のこと
文字 | 概要 |
---|---|
\n | 改行(LF: Line Feed) |
\r | 復帰(CR: Carriage Return) |
\ | バックスラッシュ |
◆テンプレート文字列 ES2015
・バッククォートを用いることで改行文字をエスケープ処理なしで記述できる
・「${…}」の形式で変数(式)を文字列に埋め込むことができる
[] … ブラケット
{} … 中括弧
◆配列リテラル
配列内の個々のデータのことを「要素」と呼ぶ
// アクセス方法
console.log(data[1][0]);
◆オブジェクト(連想配列)リテラル
配列内の個々のデータのことを「プロパティ」と呼ぶ
// アクセス方法
console.log(data.name);
console.log(data['name']);
■演算子
演算子(Operator:オペレーター) … 「+」や「-」、「++」等のこと
非演算子(Operand:オペランド) … 演算子によって処理される変数/リテラルのこと
◆インクリメント演算子/デクリメント演算子
x++ = x = x + 1
x-- = x = x - 1
◆前置演算
Pre Increment … オペランドの前方にインクリメント演算子を置くこと
Pre Decrement … オペランドの前方にデクリメント演算子を置くこと
◆後置演算
Post Increment … オペランドの後方にインクリメント演算子を置くこと
Post Decrement … オペランドの後方にデクリメント演算子を置くこと
// 前置演算
var x = 3;
var y = ++x;
// (1) xに1を足す
// (2) 計算後の値がyに代入される
// x: 4 y: 4
// 後置演算
var x = 3;
var y = x++;
// (1) xをyに代入(計算前の値)
// (2) xに1を足す(yには影響なし)
// x: 4 y: 3
◆小数点を含む計算の注意点
console.log(0.2 * 3); // 結果:0.6000000000000001
JavaScriptは内部的には数値を10進数ではなく2進数で演算している。
0.2は2進数では0.00110011....という無限循環小数となる。
そのため誤差が生じてしまう。
▼対処法
- 値を一旦整数にしてから演算する
- 1.の結果を再び小数点に戻す
◆代入演算子
x ●= y = x = x ● y
x += y = x = x + y(xにyを足してxに代入)
◆基本型と参照型による代入の違い
var data1 = [0, 1, 2];
var data2 = data1;
data1[0] = 5;
console.log(data2);
// 結果:[5, 1, 2]
参照型の場合、値そのものではなく値を格納しているアドレスが変数に格納される。
変数data1とdata2は双方ともに同じ値を見ているので、
data1への変更はそのままdata2にも影響を及ぼすことになる。
このような代入を「参照による代入」という。
◆分割代入(配列) ES2015
let data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let [d0, d1, d2, d3, d4, d5, d6, d7, d8] = data;
console.log(d8) // 結果:9
let num = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let [n0, n1, n2, ...other] = num;
console.log (other) // 結果:[4, 5, 6, 7, 8, 9]
配列に一度にまとめて代入することができる。
また、「...」演算子(スプレッド構文)を使用すると、残りの要素をまとめて扱うことができる。
◆分割代入(オブジェクト) ES2015
オブジェクトのプロパティを変数に分割することができる。
▼一つだけ取り出す場合
let book = {
title: 'Javaポケットリファレンス',
publish: '技術評論社',
price: 2680
}
let { title } = book;
console.log(title); // Javaポケットリファレンス
▼複数取り出す場合
let book = {
title: 'Javaポケットリファレンス',
publish: '技術評論社',
price: 2680
}
let { price, title, publish } = book; // 順番がバラバラでも取り出すことができる
console.log(title); // Javaポケットリファレンス
console.log(price); // 2680
console.log(publish); // 技術評論社
▼取り出し先に変数に対応するプロパティが無い場合/デフォルト値を設定したい場合
let book = {
title: 'Javaポケットリファレンス',
publish: '技術評論社',
price: 2680
}
let { price, title, memo = 'なし'} = book; // デフォルト値の設定ができる(後述)
console.log(title); // Javaポケットリファレンス
console.log(price); // 2680
console.log(memo); // なし
・左辺に対応する変数がないpublishプロパティは無視される。( console.log(publish)
はエラーになる)
・目的のプロパティが存在しなかった場合に備えて「 変数 = デフォルト値 」の形式でデフォルト値を設定することができる。
(上記の場合、変数memoに対応するmemoプロパティが存在しないため、デフォルト値として「なし」が適用される)
(1) 入れ子となったオブジェクトを分解する
・単にotherとした場合にはotherプロパティの内容(オブジェクト)がまとめて格納される。
・ other: { keywd }
とすると other - keywd プロパティの値が格納される。
let book = {
title: 'Javaポケットリファレンス',
publish: '技術評論社',
price: 2680,
other: {
keywd: 'Java SE 8',
logo: 'logo.jpg'
}
};
let { title, other, other: { keywd } } = book;
console.log(title); // Javaポケットリファレンス
console.log(other); // {keywd: "Java SE 8", logo: "logo.jpg"}
console.log(keywd); // Java SE 8
(2) 変数の別名を指定する
「 変数名
: 別名
」の形式で、プロパティとは異なる名前の変数に値を割り当てることができる。
let book = {
title: 'Javaポケットリファレンス',
publish: '技術評論社'
};
let { title: name, publish: company } = book;
console.log(name); // Javaポケットリファレンス
console.log(company); // 技術評論社
◆論理演算子
falsyな値 … 暗黙的にfalseとみなされる値
・空文字列("")
・数値の0
・NaN(Not a Number)
・null
・undefined
ショートカット演算
「&&」演算子の場合、左式がfalseと評価された時点で条件式自体は必ずfalseとなるため、右式は評価(実行)されない。
このような演算のことを ショートカット演算 や 短絡演算 という。
x === 1 && console.log('こんにちは');
// 上と下は意味的に等価
if(x === 1) { console.log('こんにちは'); }
しかし上記のような使用方法は避けるべき。
理由:右式が実行されるかどうかが曖昧になるため、思わぬバグの温床になりかねないから
◆その他の演算子
演算子 | 概要 |
---|---|
, | 左右の式を続けて実行 |
delete | オブジェクトのプロパティや配列の要素を削除 |
instanceof | オブジェクトが指定されたクラスのインスタンスかを判定 |
new | 新しいインスタンスを生成 |
typeof | オペランドのデータ型を取得 |
void | 未定義地を返す |
delete演算子
オペランドに指定した変数や配列要素、オブジェクトのプロパティを破棄する。
削除に成功した場合はtrue、失敗した場合はfalseを返す。
▼配列の要素を削除
var ary = ['1つ目の要素', '2つ目の要素', '3つ目の要素'];
console.log(delete ary[0]); // 結果:true(削除に成功)
console.log(ary); // 結果:[1: "2つ目の要素", 2: "3つ目の要素"]
配列の要素を削除した場合、該当する要素が削除されるだけで、後ろの要素が繰り上がるわけではない
▼オブジェクト(連想配列)のプロパティを削除
var obj = { x:1, y:2 };
console.log(delete obj.x); // 結果:true(削除成功)
console.log(obj.x); // 結果:undefined(削除されたため未定義値に)
console.log(obj); // 結果:{ y:2 }( x:1 が削除されたため y:1 のみになる)
var obj2 = { x:obj, y:2 };
console.log(obj); // 結果:{ y:2 }(「x:1」が削除されたため「y:1」のみになっている)
console.log(obj2); // 結果:{ x: {y: 2}, y:2}
console.log(delete obj2.x); // 結果:true(削除に成功)
console.log(obj); // 結果:{ y:2 }(変わらず)
console.log(obj2); // 結果:{ y:2 }(「x: {y: 2}」が削除されたため「y:2」のみになっている)
console.log(obj2.x); // 結果:undefinde(削除されたため未定義値に)
プロパティを削除した場合でも、プロパティそのものが削除されるだけで、プロパティが参照するオブジェクトが削除されるわけではない
( obj2.x
が削除されても、 obj2.x
が参照している obj
は削除されない)(ややこしい)
▼変数を削除
var data1 = 1;
console.log(delete data1); // 結果:false(削除に失敗)
console.log(data1); // 結果:1(明示的に宣言された変数は削除できない)
data2 = 1;
console.log(delete data2); // 結果:true(削除に成功)
console.log(data2); // 結果:エラー(data2は存在しない)
明示的に宣言された変数を削除することはできない。
■制御構文
while/do...while … 条件式の真偽に応じてループを制御する
for … 指定された回数だけ処理を繰り返す
for...in … 指令されたオブジェクト(連想配列)の要素を取り出して先頭から順に処理する
◆while
var x = 8;
while(x < 10) {
console.log('xの値は' + x);
x++;
}
// 結果:「xの値は8」「xの値は9」を順に出力
前置判定 … 判定 ⇒ 処理
◆do...while
var x = 8;
do {
console.log('xの値は' + x);
x++;
} while(x < 10);
// 結果:「xの値は8」「xの値は9」を順に出力
後置判定 … 処理
⇒ 判定
※do...while分の末尾にはセミコロンが必要
◆for
指定回数だけループを処理する
var x = 8;
for(var x = 8; x < 10; x++) {
console.log('xの値は' + x);
}
// 結果:「xの値は8」「xの値は9」を順に出力
x++ ⇒ 1ずつ加算
x-- ⇒ 1ずつ減算
x += 2 ⇒ 2ずつ加算
◆for...in
連想配列の要素を順に処理する
for(仮変数 in 連想配列) {
ループ内で実行する命令(群)
}
var data = { apple: 150, orange: 100, banana: 120 };
for (var key in data) {
console.log(key + '=' + data[key]);
}
// 結果
// apple=150
// orange=100
// banana=120
※配列ではfor...in命令は利用しない
◆for...of
配列など(※)を順に処理する
配列など = 「列挙可能なオブジェクト」のこと
「列挙可能なオブジェクト」…
・配列
・Arrayライクなオブジェクト(NodeList、arguments)
・イテレーター/ジェネレーター
for (仮変数 of 列挙可能なオブジェクト) {
ループ内で実行する命令(郡)
}
var data = [ 'apple', 'ogange', 'banana' ];
Array.prototype.hoge = function() {}
for (var value of data) {
console.log(value);
}
// 結果:「apple」「orange」「banana」「undefined」を順に出力
for...in文 → 仮変数に key (キー名(インデックス番号))
for...of文 → 仮変数に value (値)
◆break命令
・while
・do...while
・for
・for...in
・for...of
通常、上記の命令は、あらかじめ決められた終了条件を満たしたタイミングでループを終了します。
しかし、「特定の条件を満たした場合に、ループを強制的に中断したい」というケースもあります。
そのようなケースで利用できるのが、 break文です。
var result = 0;
for (var i = 1; i <= 100; i++) {
result += i;
if (result > 1000) { break; }
}
console.log('合計値が1000を超えるのは' + i + '回目');
// 結果: 合計値が1000を超えるのは45回目
◆continue命令
所感:「continue」という名前だが、意味的には「スキップ&コンティニュー」
ループを完全に中断してしまうのではなく、「現在のループだけをスキップして、次のループを継続して実行したい」という場合はcontinue命令を使用します。
var result = 0;
for (var i = 1; i < 100; i++) {
if (i % 2 === 0) { continue; }
result += i;
}
console.log('合計:' + result);
// 結果: 合計:2500
上記は、変数iを1〜100の間で奇数のみ加算し、その合計値を求めるためのサンプルです。
ここではカウンター変数iが偶数(=変数iが2で割り切れる)の場合にのみ処理をスキップすることで、奇数だけの合計値を求めています。
◆ラベル構文
ネストされたループの中で 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 />');
}
// 結果:
// 1 2 3 4 5 6 7 8 9
// 2 4 6 8 10 12 14 16 18
// 3 6 9 12 15 18 21 24 27
// 4 8 12 16 20 24 28
// 5 10 15 20 25 30
// 6 12 18 24 30
// 7 14 21 28
// 8 16 24
// 9 18 27
// 1×1、1×2、1×3、1×4、1×5、、、、(30より大きい場合はスキップ)