##1. はじめに
これまで記事にしてきたJavaScriptの学習に関して、抜けがあった分を記載する。
今回は「関数」に関して記述していきたい。
【2021/11/24追記】 「JavaScriptにおけるクラスの定義」を追加!
過去の関数に関する記事一覧 |
---|
はじめてのJavaScript⑭ 「関数」 |
はじめてのJavaScript⑮ 「function命令」 |
はじめてのJavaScript⑯「関数リテラル」 |
はじめてのJavaScript⑰ 「Functionコンストラクタ」 |
はじめてのJavaScript⑱ 「関数の演習」 |
##2. アロー関数 |
###概要 |
ES6から新しく導入された関数記法で、名前の通り「=>」を使って関数を定義する。 |
無名関数を簡略化するために考案されたもので、書き方が似ている。 |
###関数の構文 |
アロー関数の基本構文は以下の通り。 |
アロー関数の基本構文
//アロー関数の宣言構文
(引数1, 引数2, ..., 引数N) => {
//アロー関数の処理
};
//アロー関数を変数に代入する形
let 変数名 = (引数1, 引数2, ..., 引数N) => {
//アロー関数の処理
};
変数名();
// 関数の呼び出し
アロー関数を変数に代入
let averageHeight = (a, b, c) => {
let totalHeight = a + b + c;
return totalHeight / 3;
};
console.log(averageHeight(160, 165, 170));
【実行結果】
165
####引数がない場合のアロー関数の書き方
引数がない場合のアロー関数
let greeting = () => {
console.log('Hello, World!')
};
greeting();
【実行結果】
Hello, World!
####引数がひとつだけの場合
引数がひとつだけ
let greeting = (name) => {
console.log('Hello,' + name);
};
greeting('K!');
【実行結果】
Hello, K!
####処理文が一行の場合 アロー関数の処理文が戻り値を使った一行だけの場合は、 「{}」と「return」を省略して書くことが出来る。 下記の条件を基に、省略する場合とそうでない場合の違いを記載する。
変数totalHeightに対し引数a,bを渡す。
コンソールへ出力する際に変数にそれぞれ値を渡したものをそれぞれの引数に返す。
#####省略しない場合
省略しない場合
let totalHeight = (a, b) => { return a + b; };
console.log(totalHeight(160, 170));
【実行結果】
330
#####省略する場合
省略する場合
let totalHeight = (a, b) => a + b;
console.log(totalHeight(160, 170));
【実行結果】
330
※スコープとは、
影響範囲のこと。
(例)「変数のスコープ」と出てきたら「その変数が使える範囲」…など。
このサイトでわかりやすく解説されている。
しかしアロー関数では、アロー関数が定義された段階でthisが固定化される。 つまり、アロー関数のthisが定義時に設定された対象のまま変化することがないということ。 言葉だけではイメージしづらいので、例を挙げて解説していく。
####thisを使ったアロー関数の例 2つの例を挙げる。 例の内容は、無名関数とアロー関数でthisを使用する。 #####無名関数
無名関数のコード
window.color = 'red';
//グローバル変数としてcolorを定義
let sampleFunc = function() {
console.log(this.color);
};
sampleFunc();
let obj1 = {
color: 'yellow',
func: sampleFunc
};
let obj2 = {
color: 'blue',
func: sampleFunc
};
obj1.func();
obj2.func();
【実行結果】
red
yellow
blue
無名関数をアロー関数に置き換えてみると、thisが固定されていることがわかる。
######つまりどういうことだってばよ?
sampleFunc関数内でのthis.color
の値は、呼び出し元がobj1かobj2なのかで異なることになる。
thisの値が関数を呼び出すときに決定されていて、関数を定義した段階ではthisは何者なのか決まっていないことがわかる。
誰に呼び出されるかでご機嫌がコロコロ変わるとか、仕事の出来ない上司の太鼓持ちみたいd(ry
#####アロー関数
アロー関数のコード
window.color = 'red';
//グローバル変数としてcolorを定義
let arrowFunc = () => {
console.log(this.color);
};
arrowFunc();
let obj1 = {
color: 'yellow',
func: arrowFunc
};
let obj2 = {
color: 'blue',
func: arrowFunc
};
obj1.func();
obj2.func();
【実行結果】
red
red
red
###コンストラクタを持たない ####コンストラクタとは? :::note 関数を使って定義されたオブジェクトのこと。 ::: 通常は、「new」を使ってオブジェクトを生成することが出来る。 雛形としてオブジェクトを定義し、その都度生成することが可能。 こちらも[thisの例](https://qiita.com/Stack_up_Rising/items/486de1a2374785d6a10e#this%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%82%A2%E3%83%AD%E3%83%BC%E9%96%A2%E6%95%B0%E3%81%AE%E4%BE%8B)で挙げたように無名関数とアロー関数で例題を挙げていく。 ####無名関数を使ったコンストラクタの例
無名関数を使ったコンストラクタ
let Info = function (id, name) {
this.id = id; //引数(id)を格納
this.name = name; //引数(name)を格納
};
let getInfo = new Info(1, 'K'); //コンストラクタの生成
console.log(getInfo.id, getInfo.name);
【実行結果】
1, 'K'
アロー関数を使ったコンストラクタ
let Info = (id, name) => {
this.id = id; //引数(id)を格納
this.name = name; //引数(name)を格納
};
let getInfo = new Info(1, 'K'); //コンストラクタの生成
console.log(getInfo.id, getInfo.name);
【実行結果】
'Uncaught TypeError: Info is not a constructor'
###argumentsを持たない ####argumentsとは? :::note 関数内のみで利用できるオブジェクトのこと。 ::: 通常は、配列のように「aruguments[i]」という形で関数に渡された値をすべて取得することが出来る。 こちらも[thisの例](https://qiita.com/Stack_up_Rising/items/486de1a2374785d6a10e#this%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%82%A2%E3%83%AD%E3%83%BC%E9%96%A2%E6%95%B0%E3%81%AE%E4%BE%8B)で挙げたように無名関数とアロー関数で例題を挙げていく。 ####無名関数を使ったarugumentsの例
無名関数を使ったaruguments
let sampleFunc = function () {
for (let i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
};
sampleFunc(1, 2, 3, 4, 5);
【実行結果】
1, 2, 3, 4, 5
アロー関数を使ったaruguments
let sampleFunc = () => {
for (let i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
};
sampleFunc(1, 2, 3, 4, 5);
【実行結果】
'Uncaught ReferenceError: arguments is not defined'
###クラスとは? :::note 端的にいえばモノの設計書のこと。 :::
HTMLのclassとはまったくの別物である。
先ほどオブジェクト指向とは、プログラミングを現実のモノのように構成するもの
だと解説したが、クラスとはこのモノのように構造する際のモノそのものの設計書にあたる。
言葉としては、以下のような意味の対応表になる。
◇オブジェクト = モノ
◇クラス = モノ(オブジェクト)の設計書
###クラスの特徴 JavaScriptにおけるクラスの書き方は特殊な書き方をする。以下は一例。
クラス変数はコンストラクタの中でのみ記載できる
クラス直下にはfunction(メソッド、関数)のみしか配置できない
など。次は使い方を説明する。
###クラスの作り方 ####クラス宣言 クラス宣言によるクラスの定義は以下のように行う。
クラス宣言によるクラスの定義
class Fooclass {
// ...クラスの内容をここで定義する
}
関数宣言時に発生するホイスティング(巻き上げ)は、クラス宣言時には発生しない。
ホイスティング(巻き上げ)とは?
コンテキスト内で宣言した変数や関数の定義をコード実行前にメモリに配置すること。
ホイスティングのことを「宣言の巻き上げ」といったりもする。
変数が宣言される前に、その変数をアクセス可能にしたり利用可能にしたりすること。
例を挙げると、
a();
function a() {
console.log('a');
}
関数の前で呼び出しても実行できるのは、コード実行の前に関数がすでにメモリ上に存在するためであり、
つまりホイスティングがあるため。
クラスを使用したい場合は必ずクラスを使用する前に、クラス宣言を行うことが必要となる。
####クラス式 クラス式によるクラスの定義は以下のように行う。
クラス式によるクラスの定義
const fooclass = class {
// ...クラスの内容をここで定義する
};
クラス式の場合、その名の通り、式としてクラスを定義することが出来る。
変数に代入するclass式は無名クラスでも名前つきクラスでも可能。
constructorで定義
const fooclass = class {
constructor(x, y) {
// コンストラクタ
this.x = x;
this.y = y;
}
};
constructor()という名前のメソッドがコンストラクタと呼ばれるものである。
コンストラクタは定義したクラスからオブジェクトを生成し、初期化する際に実行される特殊な初期化用メソッドである。
constructor()内でthis.xにconstructor()の第一引数xを代入しているが、これは
自身のプロパティxに指定された引数の値を設定する
という意味になる。(第二引数yも同様)
メソッドでの定義
const fooclass = class {
constructor(x, y) {
// コンストラクタ
this.x = x;
this.y = y;
}
calc() {
//メソッド
return this.x + this.y; // x と y を足した値を返す
}
};
上記コードのcalc()がクラス内でのメソッド定義になる。
ここでは、fooclassクラスのプロパティxとyを足した値を返すメソッドとして定義している。
このようにクラス内でコンストラクタとメソッドを定義することで、
クラスからオブジェクトを作成した際に定義したコンストラクタのプロパティとメソッドを使用することが出来る。
###クラスの使い方 クラスはクラス宣言やクラス式によって定義しただけでは役に立たない。 クラスはオブジェクト指向におけるモノの設計書であり、 役割は設計したモノを生成すること。 つまり基本的には、`オブジェクトを作成してはじめてクラスという概念が役に立つ`といえる。
使い方の順番を以下記載する。
####(1)オブジェクトの作成(new) クラスから、クラスのインスタンスであるオブジェクトを作成するためにはnew演算子を用いる必要がある。
オブジェクトの作成
let foo1 = new fooclass(10, 20);
```
:::note warn
これでfooclassのインスタンス(オブジェクト)が作成され、変数foo1に代入された。
:::
:::note warn
<strong>new演算子 + クラス + 括弧</strong>で<strong>オブジェクトが作成される</strong>。
:::
:::note warn
この際に登場する括弧は、
クラス定義の際に登場した<strong>コンストラクタ(constructor)</strong>に渡される引数を示す。
:::
</div></details>
####(2)プロパティの取得と設定
クラスのプロパティは以下のように呼び出すことが可能。
<details><summary>get構文</summary><div>
````index.js
let hooclass = class {
constructor(z) {
this.z = z;
}
get getZ() {
// get構文
return this.z;
}
};
let hoo = new hooclass(20);
let val = hoo.getZ;
console.log(val); // 20が出力される
```
クラス定義の中に<strong>get</strong>という構文がある。
↓
:::note warn
このget構文を用いることで、
<strong>オブジェクト作成後にget構文で指定したメソッドの返却値を取得できる</strong>。
:::
</div></details>
また値を取得するget構文に対し、値を設定するset構文というものが存在する。
<details><summary>set構文</summary><div>
````index.js
let hooclass = class {
constructor(z) {
this.z = z;
}
get getZ() {
return this.z;
}
set setZ(z) {
/* set構文 */
this.z = z;
}
};
let hoo = new hooclass(20);
hoo.setZ = 100; // プロパティに値を設定する
let val = hoo.getZ;
console.log(val); // 100が出力される
```
:::note warn
set構文で指定された上記コード中の<strong>valueOfZ()メソッド</strong>を使用すると、
<strong>インスタンス作成後にそのインスタンスのプロパティに値を設定することが出来る</strong>。
:::
:::note warn
ここでははじめ、コンストラクタによりプロパティzを20で初期化したインスタンスが、
<strong>set構文で指定したメソッドvalueOfZに100を渡すことで、プロパティzを100に再設定している</strong>。
:::
</div></details>
####(3)継承
オブジェクト指向における代表的な概念のひとつに<strong>継承</strong>がある。
継承とは、
`クラス間に親子関係を作り出し、親のクラスが保有するメソッドやプロパティを使用できるようにするもの`
である。
継承を行うには<strong>extends</strong>を利用します。
<details><summary>継承</summary><div>
````index.js
let hooclass = class {
constructor(z) {
this.z = z;
}
get getZ() {
return this.z;
}
set setZ(z) {
// set構文
this.z = z;
}
};
// hooclassの継承 */
let taaclass = class extends hooclass {
constructor(z, num) {
super(z); // 親クラスのコンストラクタ
this.num = num;
}
calc() {
return this.z * this.num;
}
};
let taa = new taaclass(20, 3);
taa.setZ = 100; // 親クラスのメソッドでプロパティに値を設定する
console.log(taa.calc()); // 300が出力される
```
:::note warn
クラス作成時、上記コードのように
<strong>extends+“継承したいクラス名”</strong>で親クラスを指定することが出来る。
:::
:::note warn
継承する親クラスのプロパティを初期化するために、
constructorには<strong>親クラスのコンストラクタを示すsuper()の引数</strong>に
初期化したい値を指定する必要がある。
:::
</div></details>
####(4)静的メソッド
クラスのインスタンスではなく、
<strong>クラスそのものから呼び出しを行う</strong>`静的メソッド`というものが存在する。
<details><summary>静的メソッド</summary><div>
```index.js
let hooclass = class {
static writeDescription() {
// staticをつけることで静的メソッドを定義できる
return 'これが静的メソッドです。';
}
};
console.log(hooclass.writeDescription()); // 静的メソッド実行
```
上記コード中のwriteDescription()メソッドの定義箇所を見てほしい。
メソッド名の前に`static`とあるが、
:::note warn
これは、<strong>ここで定義するメソッドが静的メソッドであることを示している</strong>。
:::
:::note warn
静的メソッドはクラスのインスタンスではなく、
<strong>クラスそのものから呼び出しを行う</strong>ため、
<strong>クラス名や静的メソッド名()のみで実行することができる</strong>。
:::
</div></details>
##5. おわりに
現在、[「配列を操作するメソッド」]()という記事を作成中で、今回記事にした関数は
死ぬほど使うので、徹底的に覚えるようにしていきたい。
【2021.11.24追記】
コンストラクタは、後々Reactとかタヒぬほど使うので、しっかり覚えていきたい。