LoginSignup
0
2

More than 1 year has passed since last update.

JavaScriptまとめ

Posted at

1. 概要

今まで業務でJavaScriptは使用したことはないが、PFで使用する事にした
後で見返すときに簡単に理解出来るようにまとめる
りあクト!という書籍で勉強した

2. 変数宣言について

JSでは昔から使用されているvarがある
変数宣言では、varは使わず、letもしくはconstで宣言する
以下で変数名と再代入、再宣言の可否をまとめた

変数名 再代入 再宣言
var 可能 可能
let 可能 不可
const 不可 不可

varのさらなるデメリットとして
変数スコープは関数単位である
他の大多数の言語ではブロックスコープであるため、挙動が読みにくい

3. プリミティブ型について

3.1. プリミティブ型とは

オブジェクトではなく、メソッドをもたないデータの事
プリミティブ型のデータは、それ自体を変更することは出来ず、代入する事でしか新しい値にできない
又、プリミティブ型を定義するときはリテラルを使用する必要がある

3.2. プリミティブ型一覧

3.2.1. Boolean

TrueおよびFalseの二つの真偽値を扱うデータ型
truefalse真偽値リテラルで定義出来る

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. 配列リテラル

sample.js
const Arr = [0, 1, 2]; //リテラルで定義
const Arr2 = new Array(1, 2, 3); //Arrayオブジェクトをコンストラクタで定義

当然だが、配列リテラルでは、配列の要素を直接指定する事になる
よって、Arrayオブジェクトとはこの様な差が生まれる

sample.js
const Arr = [3]; //Arr>>[3];
const Arr2 = new Array(3); //Arr2>>[empty,empty,empty];

3.4.2. オブジェクトリテラル

sample.js(オブジェクトリテラルの宣言)
const obj = {
  name: 'Islanders-Treasure',
  age: 26

定義したオブジェクトリテラルはobj.keyまたはobj[key]で取得できる

sample.js
obj.name
>> 'Islanders-Treasure'
obj['age']
>>26

3.4.3. 正規表現リテラル

/patern/fragsの形式で記述する
正規表現パターンでの特殊文字の使い方は他の言語と同じ
RegExpオブジェクトのインスタンスとして生成される

sample.js
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)、関数呼び出しの事
  • 指揮と演算子の組み合わせも式という
sample.js
const cnt = 'sample';

4.1.2. 文

  • 変数に代入できないもの
  • 何らかの手続きを処理系に命令するもの
sample.js
const isTrue = true;
if (isTrue) {
}

4.3. セミコロンつける場所

4.3.1. 文の場合

  • 文の末尾にはセミコロンが必要
  • {}ブロックで終わる場合にはつけないという例外規則がある

4.3.2. 式の場合

  • 逆に関数式のほうは最終的に変数への代入文になっているため末尾にセミコロンをつける必要がある

5. 第一級オブジェクトとは

以下のような特徴を有するオブジェクトのこと

  • 変数への代入
sample.js
let aaa = () => console.log('Hello');
aaa(); //>>Helo
  • 配列の要素やオブジェクトのプロパティ値になれる
sample.js
let obj = {
  name: 'aaa',
  age: () => 10 * 3;
}
  • 他の関数に引数として渡す事ができる
  • 別の関数の戻り値として設定できる

JavaScriptでは、

関数は第一級オブジェクトとして扱われる

6. アロー関数

JavaScriptの関数式

  • 従来の関数の代替構文
sample.js(従来の関数)
const addOne = function (n) {
  return n + 1;
};
  • 従来の関数をアロー関数に書き換え
sample.js(アロー関数)
const addOne = (n) => {
return n + 1;
};
  • 本文がreturn文だけの時はreturn文をブロックごと省略できる
sample.js(アロー関数:{}なし)
const addTwoArgs = (a, b) => return n + 1;
  • 引数がひとつの時は引数の()も省略できる
sample.js(アロー関数:()&{}なし)
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に扱いたいときも直感的に書くことができる

オブジェクトに対する操作がより直感的になる
nullundefinedとの闘いが楽になる

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のコンテキストを変更することができる
下の例では、samplesamplefuncメソッドのコンテキストは、メソッドのプロパティをもっているオブジェクトであるsampleになる
よって、sample.samplefunc();ではuserと出力される
それを、applythisのコンテキストをsample2に変更することでthis.namesample2.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モード有効の時)

メソッドでない関数のthisundefinedが入る仕様になった
関数のコンテキストがグローバルオブジェクトの場合は参照しないことになった

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. 教訓

  • クラス式内で関数を使用する時は、挙動が直感的に理解しやすいアロー関数を使用すること

  1. ブラウザ実行の場合。Node.jsの場合はglobalその他の環境ではまた別のグローバルオブジェクトがある 参考:グローバルオブジェクト 

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2