1. 概要
今まで業務でJavaScript
は使用したことはないが、PFで使用する事にした
後で見返すときに簡単に理解出来るようにまとめる
りあクト!という書籍で勉強した
2. 変数宣言について
JSでは昔から使用されているvar
がある
変数宣言では、var
は使わず、**let
もしくはconst
**で宣言する
以下で変数名と再代入、再宣言の可否をまとめた
変数名 | 再代入 | 再宣言 |
---|---|---|
var | 可能 | 可能 |
let | 可能 | 不可 |
const | 不可 | 不可 |
var
のさらなるデメリットとして
変数スコープは関数単位である
他の大多数の言語ではブロックスコープであるため、挙動が読みにくい
3. プリミティブ型について
3.1. プリミティブ型とは
オブジェクトではなく、メソッドをもたないデータの事
プリミティブ型のデータは、それ自体を変更することは出来ず、代入する事でしか新しい値にできない
又、プリミティブ型を定義するときはリテラルを使用する必要がある
3.2. プリミティブ型一覧
3.2.1. Boolean
TrueおよびFalseの二つの真偽値を扱うデータ型
true
とfalse
の真偽値リテラルで定義出来る
###3.2.2. Number型
数値を扱うためのデータ型
整数も小数も同じNumber
型になる
数値リテラルで定義出来る(-9や36など)
先頭に0x
をつけることで16進数を表現できる
0o
をつけることで8進数を表現できる
0b
をつけることで2進数を表現できる
3.14
などの形式は不動小数点リテラルと呼ぶ
3.2.3. BigInt型
Number
型では扱えない数値を扱うデータ型
Number
型とは互換性がないので代入や計算、等値比較はできない
100n
のように数字の後ろにnをつけて表現する数値リテラルで定義出来る
3.2.4. String型
文字列を扱うためのデータ型
''
、または""
で囲む文字リテラル
バッククウォート
を用いると改行を含む複数行テキストや${}
による式展開が可能なテンプレートリテラルになる
3.2.5. Symbol型
シンボル値という固有の識別子を表現する値
オブジェクトのプロパティキーとして使用可能
3.2.6. Null型
プリミティブ値Nullは何のデータも存在しない状態を明示的に表す際に使用する
nullリテラルであるnull
はプリミティブ型null
を返す
しかし、typeof
演算子でnull
のデータ型を調べるとobject
となるらしい
3.2.7. Undefined型
宣言のみが行われた変数や、オブジェクト内の存在しないプロパティへのアクセスに割り当てられる
他の多くの言語と異なり、Nullと明確に区別される
undefined
というのは実際はグローバル変数である
3.3. プリミティブ値のリテラルとラッパーオブジェクトについて
プリミティブ型ではインスタンスメソッドを持たないはずが、実際は使用できる
なぜか
ラッパーオブジェクトのおかげ である
本当ははラッパーオブジェクトはnew
演算子と対応するコンストラクタ関数を利用している作成する事になる
だが、明示的にラッパーオブジェクトを指定しなくてもプリミティブ型のデータに対してアクセスする時はその対応するラッパーオブジェクトに自動変換するという仕組みがある
この仕様によって、明確に異なるプリミティブ型とオブジェクト型の間で同じインスタンスメソッドが利用できる
3.4. オブジェクト型のリテラルについて
上記のプリミティブ型に対応するリテラル以外のリテラルを見ていく
3.4.1. 配列リテラル
const Arr = [0, 1, 2]; //リテラルで定義
const Arr2 = new Array(1, 2, 3); //Arrayオブジェクトをコンストラクタで定義
当然だが、配列リテラルでは、配列の要素を直接指定する事になる
よって、Array
オブジェクトとはこの様な差が生まれる
const Arr = [3]; //Arr>>[3];
const Arr2 = new Array(3); //Arr2>>[empty,empty,empty];
3.4.2. オブジェクトリテラル
const obj = {
name: 'Islanders-Treasure',
age: 26
定義したオブジェクトリテラルはobj.key
またはobj[key]
で取得できる
obj.name
>> 'Islanders-Treasure'
obj['age']
>>26
###3.4.3. 正規表現リテラル
/patern/frags
の形式で記述する
正規表現パターンでの特殊文字の使い方は他の言語と同じ
RegExp
オブジェクトのインスタンスとして生成される
let re = /ab+c/;
3.5. JavaScriptにおけるオブジェクト
広義のオブジェクトと狭義のオブジェクトという風にコンテキストによって意味が変わる
3.5.1. 狭義のオブジェクトの定義
一般的なクラスベースのオブジェクト指向言語では
クラスインスタンス
のことを**「オブジェクト」**と呼称する
javascriptでのオブジェクトは
キーとそれに対応する値を持ったプロパティの集まり
- 一般的には連想配列と呼ばれる
- Rubyのハッシュに相当するもの
3.5.2. 広義のオブジェクトの定義
プリミティブ値以外のすべてのものを包括して指す
- 広義のオブジェクトはすべて
Object
という標準組み込みオブジェクトを最終的な継承元に持っている - プリミティブ型はオブジェクトではない(
Object
オブジェクトを継承しているのではなく、あくまでリテラル値で表現される値であるため)
4. 関数の定義について
4.1. プログラムにおける文と式違い
4.1.1. 式
- 値を生成し、変数に代入できる物
- 具体的には、リテラル(
sample
)、変数(foo
)、関数呼び出しの事 - 指揮と演算子の組み合わせも式という
const cnt = 'sample';
4.1.2. 文
- 変数に代入できないもの
- 何らかの手続きを処理系に命令するもの
const isTrue = true;
if (isTrue) {
}
4.3. セミコロンつける場所
4.3.1. 文の場合
- 文の末尾にはセミコロンが必要
-
{}
のブロックで終わる場合にはつけないという例外規則がある
4.3.2. 式の場合
- 逆に関数式のほうは最終的に変数への代入文になっているため末尾にセミコロンをつける必要がある
5. 第一級オブジェクトとは
以下のような特徴を有するオブジェクトのこと
- 変数への代入
let aaa = () => console.log('Hello');
aaa(); //>>Helo
- 配列の要素やオブジェクトのプロパティ値になれる
let obj = {
name: 'aaa',
age: () => 10 * 3;
}
- 他の関数に引数として渡す事ができる
- 別の関数の戻り値として設定できる
JavaScriptでは、
関数は第一級オブジェクトとして扱われる
6. アロー関数
JavaScriptの関数式
- 従来の関数の代替構文
const addOne = function (n) {
return n + 1;
};
- 従来の関数をアロー関数に書き換え
const addOne = (n) => {
return n + 1;
};
- 本文が
return
文だけの時はreturn文をブロックごと省略できる
const addTwoArgs = (a, b) => return n + 1;
- 引数がひとつの時は引数の()も省略できる
const addOne = n => n + 1;
7. 無名関数(匿名関数)
関数の名前は、関数式などで定義したものについては、functionインスタンスとしてのnameプロパティに格納されている
定義時に名前を与えられない関数を無名関数と呼ぶ
名前付き関数自体は、関数で閉じたローカル変数なので、外部から呼び出すことはできない
関数の中でのみ呼び出すことができる
let x = 0;
const sampleFunc = function localFunc(){
if(x === 0){
++x;
console.log('Hello');
localFunc();
}
};
sampleFunc();
// >>Hello
localFunc();
// >>エラー
7.1. アロー関数でのnameプロパティ
(() => {}).name ではnameプロパティは無い
名前が無いのでメモリに残らず、変数に代入されなければ定義しても参照できない
無名関数は変数に代入することで、nameプロパティに変数名が格納されるのでメモリに保存される
() => console.log('hello'); //このままではメモリに残らないので、どこからも参照できない
const sampleFunc = () => console.log('hello');
sampleFunc(); // >>hello
8. 引数の表現
8.1. デフォルト引数
関数の引数に対してデフォルト引数を設定できる
VB
でいう引数のOptional
での宣言みたいなもん
const multiply(a, b) = (a, b=10) => a * b;
console.log(multiply(2,1); // >>2
console.log(multiply(2); // >>20
デフォルト引数を設定するとき
デフォルト値がある引数は引数の後ろにまとめて設定すること
関数の設計として重要な値ほど前に持ってくるべきだから
8.2. レストパラメーター
残りの引数を配列として受け取ることが出来るもの
関数の最後の引数の名前に「...」のプレフィックスをつけることで残りの引数を配列で受け取ることができる
const sample = (a, b, ...c) => {
let ans = a + b;
c.map(element => ans += element);
console.log(ans);
};
sample(1,2,3,4,5,6);
// >>21
レストパラメータにそれぞれ名前をつけたい場合
const sum = (i, …[j, k, l]) => i + j + k + l;
ただし、定義してある以上の引数が受け渡された場合は切り捨てられる
9. クラス構文
実態は特別な関数である
JSのクラスにはクラス式とクラス宣言がある
9.1. クラス宣言
class sample{
constructor(name, age){
this.name = name;
this.age = age;
}
}
9.2. クラス式
const aaa = class {
constructor(height,width) {
this.height = height;
this.width = width;
}
//ゲッター
get area() {
return this.calcArea();
}
//インスタンスメソッド
calcArea () {
return this.height * this.width;
}
//静的メソッド(クラスメソッド)
static sayHello() {
console.log('Hello');
}
};
const sample = new aaa(10,20);
console.log(sample.area);
// >>200
aaa.sayHello();
// >>"Hello"
extends
で親クラスを継承した子クラスを作ることができる(他のオブジェクト指向言語と同じ)
super
で継承元のクラスにメンバー変数を渡すことができる
JavaScriptにおけるコンストラクタ関数とは
プロトタイプオブジェクトを継承してオブジェクトインスタンスを生成するための独立した関数のこと
10. プロトタイプベースのオブジェクト指向
プロトタイプベースではオブジェクトの抽象としてのクラスが存在しない
10.1. 「オブジェクトの抽象としてのクラスがある」とは
クラスベースでは、オブジェクトの下になるクラスが存在する
これをもとにオブジェクトを作成する
これを「オブジェクトの抽象としてのクラスがある」と表現する
10.2. プロトタイプベースでのオブジェクト
プロトタイプベースでは、オブジェクトの抽象となるのはクラスではない
オブジェクトの抽象となるのはほかのオブジェクトを継承する
その時の継承元になったオブジェクトをオブジェクトプロトタイプと呼ぶ
10.3. コンストラクタ関数とは
広義のオブジェクトに設定されているプロトタイプオブジェクトを継承して新しくオブジェクトインスタンスを生成するための関数
const arr = new Array(3); //arrというArrayオブジェクトを定義するためのコンストラクタ関数はArray
const str1 = new String('sample'); //str1というStringオブジェクトを定義するためのコンストラクタ関数はString
プリミティブ型のラッパーオブジェクト値がそれぞれ使えてるメソッドはオブジェクトインスタンスの継承元であるプロトタイプが持ってるメソッドを継承したもの
JavaScriptではあらゆるオブジェクトは何らかのプロトタイプを継承していて、その「プロトタイプチェーン」は最終的には{}(空オブジェクト)を経てnullに到達する
11. 配列やオブジェクトの便利な構文
11.1. オブジェクトのキー名と値をそれぞれ動的に設定する例(keyとbaz)
オブジェクトのキーで変数を参照する際は[key]のように[]で囲む必要がある
const key = 'bar';
const baz = 65536;
const obj1 = { foo: 256, [key]: 4096, baz: baz };
console.log(obj1); //{ foo: 256, bar: 4096, baz: 65536 }
11.2. プロパティのショートハンド
オブジェクトのキーと値を事前に変数で定義しているときに使用できる構文
オブジェクトの中で変数 baz の名前がプロパティのキー名に、値がそのプロパティ値になってる
const baz = 100
const obj2 = { baz };
console.log(obj2); // { baz: 100 }”
11.3. 分割代入
11.3.1. 通常の分割代入
Rubyの多重代入に似たようなもの
const [n, m] = [1, 4];
console.log(n, m); // 1 4
11.3.2. オブジェクトを分割代入するときの高度なテクニック
Object
オブジェクトを定義するとき、Object
のキーと同じキーで分割代入することができる
const obj = { name: 'Kanae', age: 24 };
const { name, age } = obj;
console.log(name, age); // Kanae 24”
11.4. スプレッド構文
コレクション型のオブジェクトを展開する際に使用する構文
本質はレストパラメーターと同じ
以下で使用用途を解説する
11.4.1. 配列の展開
任意の配列を展開する際に使用する
通常は、配列内の要素に配列変数を指定すると、1つの要素として配列が存在することになる
レストパラメーターを配列の要素に指定することで、配列変数内の要素を指定の配列に展開することができる
下記例では
-
arr2
でレストパラメーターを指定して配列展開をしている -
arr3
では要素に直接配列を指定しているので、配列の要素に配列がそのまま入っている
const arr1 = ['A', 'B', 'C'];
const arr2 = [...arr1, 'D', 'E'];
const arr3 = [arr1, 'D', 'E'];
console.log(arr2); // [ 'A', 'B', 'C', 'D', 'E' ]
console.log(arr3); // [ ['A', 'B', 'C'], 'D', 'E' ]
11.4.2. オブジェクトの展開
任意のオブジェクト内に展開する際に使用する
配列と同じで、変数をそのままオブジェクトのキーに指定すると、値にオブジェクトが入るだけになってしまう
レストパラメーターを指定することで、指定のオブジェクトの中にレストパラメーターで指定したオブジェクトを展開できる
下記例では
-
arr2
でレストパラメーターを指定してオブジェクトを展開をしている -
arr3
では要素に直接オブジェクトを指定しているので、キーに変数名、値にオブジェクトがそのまま入っている
const obj1 = { a: 1, b: 2, c: 3, d: 4 };
const obj2 = { ...obj1, d: 99, e: 5 };
const obj3 = { obj1, d: 99, e: 5 };
console.log(obj2); // { a: 1, b: 2, c: 4, d: 99, e: 5 }
console.log(obj3); // { obj1: { a: 1, b: 2, c: 3, d: 4 }, d: 99, e: 5 }
上記の分割代入とスプレッド構文を組み合わせることで
分割代入で一部の値を受け取り、他の値をスプレッド構文で受け取るといった高度なことが可能になる
12. ショートサーキット評価(短絡評価)、他
他の言語と同様、複数の条件が存在する場合に左辺から評価し、真偽が決まった時点で残りを評価せずに値を返すことができる
12.1. Optional Chaining
プロパティアクセス修飾子の後に?
をつけて、指定したキーが存在しなかった時にundefinedを返すための機能
12.1.0. なぜ必要か
オプショナルなオブジェクトに対してオブジェクトチェーンを使用してアクセスするソースを書いているときに
通常であれば各階層の値がundefined
かどうかをチェックする処理を各必要がある
このチェックを行わずに下層のプロパティにアクセスするしたいから
12.1.1. 通常のオブジェクトチェーン
- 1階層目のプロパティが存在しない場合は
undefined
が返る - 1階層目にないプロパティを指定して2階層目を指定するとエラーになってしまう
通常のオブジェクトチェーンでは1階層目で存在しなくても2階層目までアクセスしてしまうため、エラーとなってしまう
1階層目であるかどうかのチェックを入れてから2階層目にアクセスする処理を入れる必要があった
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};
console.log(adventurer.dog); //undefined
if (adventurer.dog != undefined) {
console.log(adventurer.dog.name);
} //adventurer.dogがundefinedでない時だけadventurer.dog.nameにアクセスする
//console.log(adventurer.dog.name); //コメントアウトするとエラーが返る
12.1.2. オブジェクトチェーンに?.
を使用する
?.
を使うとチェーン内の各参照が正しいかどうかを明示的に確認せずにアクセスしていくことが出来る
プロパティアクセス修飾子の後に?
をつけることでオブジェクトチェーンの各階層で短絡評価をするようにできる
途中のプロパティが存在していなかったらそこで式が短絡されてundefined
が返る
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};
console.log(adventurer.dog?.name); //undefined
12.2. Nullish Coalescing
左辺がnull
又はundefined
の時に右辺が評価される機能
12.2.0. なぜ必要か
左辺が空文字
や0
の時に右辺を評価したい時がある
""
や0
は直感的には明示的に指定している値かはわからない
これらを意図的に使用している場合に、truthy
な値として評価させたいため
12.2.1. 通常の処理
通常では、処理系が0や空文字をfalsy
な値と判断するため、その場合は右辺を評価して上書きされてしまう
-
fooo
はnullなのでこれはどちらでもfalsy
な値と判断できる -
bazz
は左辺が0
であり、明示的にそうなっているときでも処理系がfalsy
と判断して上書きされる
const fooo = null || 'default string';
console.log(fooo); //'default string'
const bazz = 0 || 42;
console.log(bazz); //42
??
を使えば、より厳密にfalsy
な値を判定できる
0
や""
を明示的に指定している場合は??
を使うことで、左辺はtruthy
な値と判断して、上書きされない
-
foo
はnullなのでこれはどちらでもfalsy
な値と判断できる -
baz
は左辺が0
であり、??
を使用することでtruthy
と判断させて、左辺のみで短絡させる
const foo = null ?? 'default string';
console.log(foo); //'default string'
const baz = 0 ?? 42;
console.log(baz); //0
12.x. Optional Chaining と Nullish Coalescing の組み合わせ
上記の二つの機能を使用することで
-
?.
でオブジェクトチェーンで下層のプロパティにアクセスするときに直感的にソースを書くことができる -
??
でオブジェクトが0
や""
であり、且つそれをtruthy
に扱いたいときも直感的に書くことができる
オブジェクトに対する操作がより直感的になる
null
とundefined
との闘いが楽になる
13. thisについて
13.1. thisとは
その関数が実行されるコンテキストであるオブジェクトの参照が格納されている引数のこと
以下のようなコンテキストがある
13.1.1. 関数・メソッド内での呼び出し
この時のthis
の参照は
その関数・メソッドをプロパティとして持つオブジェクトへの参照
13.1.1.1. オブジェクトのメソッド内
以下では、sample
オブジェクトが、aaa
というキーでメソッド(プロパティ)を格納している
このメソッドの中でのthis
のコンテキストはメソッドをプロパティとして持っているsample
オブジェクトなので、this=sample
となる
var sample = {
name: "user",
aaa: function() {
console.log(this.name);
}
};
obj.aaa(); //user(this=sampleより、sample.nameとなる)
※JavaScriptにおけるプロパティとは
Object
オブジェクトでいう、特定のキーをもとに公開するデータや値のこと
以下は、sample
オブジェクトの中に、name
キーでuser
というプロパティが格納されている
const sample = { name = "user" };
13.1.1.2. 関数内
関数式を宣言しているスコープがグローバルスコープである場合
この時のthis
のコンテキストはグローバルオブジェクト(Window)1になる
const aaa = 'user'
const samplefunc = function() {
console.log(this.aaa);
};
samplefunc(); //user(this=Windowより、Window.aaaとなる)
13.1.2. コンストラクタ内での呼び出し
特定のインスタンスを生成するとき、new
演算子に続けてコンストラクタ関数を使用する(new Array
とか)
この時のthis
のコンテキストは新たに生成されるインスタンスを参照する
下の例ではsample
クラスにname
キーとage
キーで値を格納するようにしている
よって、インスタンス化するときに、コンストラクタ内のthis
が参照するのはインスタンスであるsample
となる
const sample = class {
constructor {
this.name = name,
this.age = age
}
};
const sampleinstnce = new sample('user',30);
console.log(sampleinstance.name); //user(this=sampleinstanceより、sampleinstance.nameとなる)
13.1.3. apply,callでの呼び出し
apply
、とcall
は特別なメソッドで、this
のコンテキストを変更することができる
下の例では、sample
のsamplefunc
メソッドのコンテキストは、メソッドのプロパティをもっているオブジェクトであるsample
になる
よって、sample.samplefunc();
ではuser
と出力される
それを、apply
でthis
のコンテキストをsample2
に変更することでthis.name
がsample2.name
になるのでadmin
と出力される
const sample = {
name: 'user',
samplefunc: function(){
console.log(this.name);
}
};
const sample2 = {
name: "admin",
};
sample.samplefunc(); //user
sample.samplefunc.apply(sample2); //admin
13.1.4. strictモードでのグローバルオブジェクト
ES2015
で追加された機能であるstrict
モードでは、それ以前のグローバルオブジェクトとコンテキストが異なる
13.1.4.1. ES2015以前の仕様
メソッドでない関数(オブジェクトのプロパティではない関数)は
this
のコンテキストにグローバルオブジェクトをとる
この仕様上、簡単にグローバル汚染を起こせてしまうことが問題であった
13.1.4.2. ES2015以降の仕様(strict
モード有効の時)
メソッドでない関数のthis
にundefined
が入る仕様になった
関数のコンテキストがグローバルオブジェクトの場合は参照しないことになった
13.2. JavaScriptのthisの挙動による弊害
13.2.1. クラス構文でstrict
モードの時に困ること
メソッド内で関数を使用する時が困る
strict
モードの時は、メソッド内で使用する関数にはコンテキストがない
通常のオブジェクト指向言語では、一つ外のスコープを参照することを期待するので、この挙動の違いは悩ましい
以下のprofile
で定義しているメソッドの中でsetTimeout
関数を使用している
この中の関数のthis
のコンテキストはグローバルオブジェクトとなり、通常はstrict
モードが有効なので、undefined
となる
const sample = {
name: `user`,
age: 30,
profile: function(){
console.log(`Hi, My name is ${ this.name }.`);
setTimeout(function(){
console.log(`${ this.age } years old`)
}
, 1000);
}
}
sample.profile();
//"Hi, My name is user."
//"undefined years old"
13.2.2. クラス内のメソッドでアロー関数を使用した時のthis
のコンテキスト
アロー関数のthis
のコンテキストは
関数が定義された時点で確定される
よって、上記のsetTimeout()
内の関数をアロー関数で定義すると、その時点のコンテキストであるsample
となる
const sample = {
name: `user`,
age: 30,
profile: function(){
console.log(`Hi, My name is ${ this.name }.`);
setTimeout(()=> console.log(`${ this.age } years old`)
, 1000);
}
}
sample.profile();
//"Hi, My name is user."
//"30 years old"
13.2.3. 教訓
- クラス式内で関数を使用する時は、挙動が直感的に理解しやすいアロー関数を使用すること
-
ブラウザ実行の場合。
Node.js
の場合はglobal
その他の環境ではまた別のグローバルオブジェクトがある 参考:グローバルオブジェクト ↩