じぶん対策

日々学んだことをアウトプットして備忘録にしています。

JavaScriptの基本の復習

はじめに

https://gihyo.jp/book/2016/978-4-7741-8411-1

本を読んだ感想や内容の簡単なまとめを書きます。丸写しではなくあくまでも自分用のメモなので細かい箇所については書籍をご確認ください。 JavaScriptの言語仕様についてのまとめになるので、全てを網羅することはできませんが、基本的な構文と僕が普段しない書き方等について調査したいと思います。

この本について

言語に関する入門書なので全体的にサラッと目を通す程度に読みました。 JavaScriptを業務で使用しているものの、必要となったときに調べる形が多く、まとまった時間で学習することがなかったため、本を一冊読んでみることにしました。 そのなかで参考になった箇所について自分の感想や調べたことを元にまとめたいと思います。

JavaScriptの基本

変数の定義にはvar?let?const?

var message;

初期値を設定しなかった場合は、JavaScriptはデフォルトで未定義(undefined)という特別な値を変数に割り当てる。
ちなみにJavaScriptの仕様上、変数の宣言は必須ではないですが、宣言する方が望ましいです。
理由は、スコープの違いです。 明示的に宣言されずに使用された変数は全てグローバル変数として扱われます。 グローバル変数が悪ということは言うまでもないと思うので割愛します。

ここで、var/let/constの使い分けについて少し調査してきました。

参考: https://qiita.com/cheez921/items/7b57835cb76e70dd0fc4

スコープや再代入可能かどうか、再宣言可能かどうかに差があります。 - 基本的にはconst - プリミティブ型を再代入したい場合にletを使用する。(オブジェクトや配列の場合の値の変更は再代入にはならない)

ちなみに、varのメリットはES5以下でも動くことですが、ここ最近は使われなくなってきているのかなという印象です。 この書籍においても極力letを使用するよう書かれていますが、constの方がより良いのかなと個人的には考えています。

JavaScriptにおけるデータ型

  • 基本型
  • 参照型 のに種類に分類される。

基本型...変数には値そのものが直接格納される。
参照型...変数には参照値(値を実際に格納しているメモリ上のアドレス)が格納されている。

分類 データ型 説明
基本型 number(数値) 整数、及び浮動小数
string(文字列) シングルクォート、もしくはダブルクォートで囲われた文字列
boolean(真偽型) true/falseのいずれか
null/undefined(シンボル型) 値が空、未定義であることを示す。
参照型 array(配列) [1,2,3]や["Red", "Green", "blue"]等
object(オブジェクト) {key:value}
function(関数) 一連の処理の集合

以上まとめた上で、型についてのJavaScriptの特徴をおさえておきたいと思います。

変数の型を定義するためにはリテラルと呼ばれるものを使用します。普段意識せずに使用していた単語ですが、意味としてはデータ型の値を直接記述できるような構文を指します。

number

数値リテラルはさらに、整数リテラル浮動小数点リテラルに分類されます。 整数リテラルには、以下のリテラルがあります。

浮動小数点リテラルは、指数表現もできます。
例) 3.14e5 => 3.14 * 10 ^ 5

string

  • stringのシングルクォートとダブルクォートについてはどちらを使用しても正しく動くが、文字列中に含む場合は注意する必要があります。

  • ES2015以降、テンプレート文字列と呼ばれる文字列表現が可能になりました。

例)

let name = '鈴木';
let str = 'こんにちは、${name}さん。
今日もいい天気ですね'
console.log(str);

これにより、エスケープシーケンスを使用せずとも改行を表現できるようになり、${変数名}の形式で変数の値を文字列に埋め込むことができます。 従来は「+」演算子で連結していましたが、よりシンプルに書けるようになります。

配列

配列は変数の集合です。 各要素には「配列名[インデックス番号]」の形式でアクセスすることができます。

オブジェクト

オブジェクトとは、名前をキーとしてアクセスすることのできる配列です。
ハッシュ連想配列と呼ばれる場合もありますが、JavaScriptの場合は同じものを指します。

オブジェクトリテラルの個々のプロパティへのアクセス方法はドット演算子ブラケット構文の2通りの方法があります。

  • ドット演算子 オブジェクト名.プロパティ名
  • ブラケット構文 オブジェクト名['プロパティ名']

両者の違いは、構文の見た目以外にもあり、obj.123のような書き方はできませんが、obj['123']という書き方ができます。
これはドット演算子の場合、プロパティ名は識別子とみなされるため、命名規則(先頭に数字を使用できない)に則っていないような名前が使えません。

関数

JavaScriptでは関数もデータ型として扱われます。

undefined

未定義値(undefined)は、ある変数の値が定義されていないことを表す値です。

  • ある変数が宣言済みであるものの値を与えられていない
  • 未定義のプロパティを参照しようとした。
  • 関数型の値で値が返されなかった

上記のような場合に返されます。

null

nullは該当する値が無いことを意味します。 undefinedとの違いは「空である」という状態を表すための値です。

分割代入

従来

let data = [56, 40, 26, 82, 19, 17, 73, 99];
let x0 = data[0];
let x1 = data[1];
let x2 = data[2];
//...列挙する必要がある

分割代入を用いた場合

let data = [56, 40, 26, 82, 19, 17, 73, 99];
let [x0, x1, x2, x3, x4, x5, x6, x7] = data;

右辺の配列を個々の要素に分解して変数に代入することができます。

変数の別名指定

変数名:別名という形式でプロパティとは異なる名前の変数に値を割り当てることができます。

例)

let book = { title: '題名', publish: '出版社' };
let { title: name, publish: company } = book;

console.log(name);    // 結果: 題名
console.log(company);    //結果: 出版社

上記の例ではtitle/publishプロパティを変数name/companyに代入します。

論理演算子について

他の言語同様、論理演算子が使用できます。

演算子 概要
&& 左右の式がともにtrueの場合はtrue 100 === 1000 && 1000 === 1000 // true
|| 左右の式のどちらかがtrueの場合はtrue 100 === 100 || 1000 === 500 // true
! 式がfalseの場合はtrue !(10 > 100) // true

「左式だけが評価されて、右式が評価されない」ケースには注意が必要です。
例えば、「&&」演算子の場合は左式がfalseの場合は右式は実行されません。
このような演算のことをショートカット演算または短絡演算といいます。
そのため、論理演算子の後方には関数の呼び出しや代入演算子等の値を実際に操作するような式を含める場合ではありません。

for系の処理について

これまでJavaScriptの基本的なデータ型や演算子についてまとめました。 ここでデータ型を使用するfor文についてまとめてみたいと思います。

基本のfor文とカンマ演算子

JavaScriptにおけるfor文は以下のように書きます。

for(var i = 0; i < 10; i++){
    // 処理内容
}

ここで、カンマ演算子を利用することで、初期化式、ループ継続条件式、増減式に複数の式を指定する事もできます。 カンマで区切られた式は先頭から順に実行されます。

for (var i = 1,j = 1; i < 5; i++, j++) {
    console.log('i * jは' + i * j)
}

出力結果

i + j は 1
i + j は 4
i + j は 9
i + j は 16

基本的には可読性を落とすので避けるべきですが、ブロック内の処理がごく単純な場合にはシンプルに表現することができます。

forEach命令

配列要素に対して繰り返し処理をします。 forEachは構文ではなくメソッドとして実装されており、引数荷インデックスを仕様できます。 後述するfor...ofと機能的にはほとんど同じです。 ただ、メソッドのため、continueやbreakを使用できません。

for...in命令

指定されたオブジェクト(連想配列)の要素を取り出して、先頭から順に処理します。

例)

for(仮変数 in 連想配列) {
    ループ内で実行する命令
}

注意点としては、配列に対してはfor...in命令は利用しないことです。
構文上は配列に対しても利用することは可能ですが、以下のような場合に不都合が生じます。

let data = ['apple', 'orange', 'banana'];
// 配列オブジェクトにhogeメソッドを追加
Array.prototype.hoge () function {}  // 配列の機能を拡張
for (let ley in data) {
    console.log(data[key]);
}
// 結果: 「apple」「orange」「banana」「function(){}」を順に出力

上記のコードの場合、拡張された機能までが列挙されてしまいます。

  • for...in命令では処理の順序も保証されない
  • 仮変数にはインデックス番号が格納されるだけなので、コードがあまりシンプルにならない(=値そのものでないので、かえって誤解を招く)

上記の理由から、for...in連想配列の操作に使用するにとどめましょう。

https://qiita.com/diescake/items/70d9b0cbd4e3d5cc6fce

for...of命令

ES2015で追加された構文です。
配列、及びArrayライクなオブジェクト(NodeList,argumentsなど)、イテレーター/ジェネレーターに対して使用できます。 これらは総称して列挙可能なオブジェクトと呼ばれます。

例)

let data = ['apple', 'orange', 'banana'];
Array.prototype.hoge = function() {}
for (let value of data) {
    console.log(value);
}
// 結果 : 「apple」「orange」「banana」を順に出力

for...ofでは仮変数にはキーではなく値が列挙されている点にも注意が必要です。 indexを使用する場合にはObject.entriesを使用し、keyとvalueの配列になる。

for文の選択

ここまでfor文の種類についてまとめてみました。 基本的にはfor...ofを使用するのが望ましそうです。

  • for ... ループ変数を柔軟に制御する必要のあるものに使用するが、記述が複雑で読みにくくなる。
  • for...in ... for...in命令では処理の順序も保証されない、keyの取得に問題がある(自身のプロパティではないものを取得してしまう)
  • forEach ... for文と比較すると記述がシンプルになる。for...ofと比較するとbreak,continue,returnの制御ができない
  • for...of ... iterableなオブジェクトならなんでも使用でき、break,continue,returnも使用できる。

イテレータとは

参考:
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Iterators_and_Generators
https://qiita.com/kura07/items/cf168a7ea20e8c2554c6

他の言語でも同様の考え方がありますが、iterableオブジェクトとはなにか整理してみます。 ES2015で追加された仕組みです。

参考Qiita記事には以下のように説明されています。

イテレータとは、「順番にイテレータリザルトを取り出すことのできるオブジェクト」のことです。

これは具体的にはnext()メソッドを持つことによって実現されます。 next()メソッドは次の2つのプロパティを持つオブジェクトを返します。

  • value ... 反復シーケンスの次の値
  • done ... シーケンスの最後の値がすでに消費されている場合にtrueとなる。

doneはイテレータが終端に到達したかどうかを示します。

イテレータを反復することを、イテレータを消費するといいます。 終了値が返されたあとはnext()を呼び出しても、単に{done: true}を返し続けます。

イテレータの例

  • Array
  • String
  • Map
  • Set

などの組み込みオブジェクトはいずれもデフォルトでイテレーターを備えているため、for...ofでは以下の要素を列挙することができます。

ユーザー定義の反復可能オブジェクト

イテレータの定義が理解できると、自作できることがわかると思います。 Symbol.iteratorキーを持つプロパティが必要となります。 MDNには、以下のような例が紹介されています。

var myIterable = {
    *[Symbol.iterator]() {
        yield 1;
        yield 2;
        yield 3;
    }
}

for (let value of myIterable) {
    console.log(value);
}
// 1
// 2
// 3

or

[...myIterable]; // [1, 2, 3]

所感

今回はJavaScriptの言語仕様について復習してみました。
JavaScriptについては業務で使用しているものの、書籍を通して読むと知らなかった構文等を発見することができます。
JavaScriptは他の言語と比べても使用しないほうがいい地雷のような使い方ができてしまう構文が多いような気がします。

書籍等を読むことで知らなかった構文を知ることで他の構文との比較ができるようになるのでその第一歩としての書籍はいい入り口かなと思います。
手を動かすことの合間に言語仕様についても復習する時間をこれからも確保したいと思います。

参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Iterators_and_Generators https://qiita.com/kura07/items/cf168a7ea20e8c2554c6