0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【JavaScript】追加学習分〜②「関数」

Last updated at Posted at 2021-11-19

##1. はじめに
これまで記事にしてきたJavaScriptの学習に関して、抜けがあった分を記載する。
今回は「関数」に関して記述していきたい。


【2021/11/24追記】 「JavaScriptにおけるクラスの定義」を追加!
過去の関数に関する記事一覧
はじめてのJavaScript⑭ 「関数」
はじめてのJavaScript⑮ 「function命令」
はじめてのJavaScript⑯「関数リテラル」
はじめてのJavaScript⑰ 「Functionコンストラクタ」
はじめてのJavaScript⑱ 「関数の演習」
##2. アロー関数
###概要
ES6から新しく導入された関数記法で、名前の通り「=>」を使って関数を定義する。
無名関数を簡略化するために考案されたもので、書き方が似ている。
###関数の構文
アロー関数の基本構文は以下の通り。
アロー関数の基本構文
index.js
//アロー関数の宣言構文
(引数1, 引数2, ..., 引数N) => {
  //アロー関数の処理
};
//アロー関数を変数に代入する形
let 変数名 = (引数1, 引数2, ..., 引数N) => {
  //アロー関数の処理
};
変数名();
// 関数の呼び出し
[無名関数](https://qiita.com/Stack_up_Rising/items/a6f0211beea531c9c057#%E9%96%A2%E6%95%B0%E3%83%AA%E3%83%86%E3%83%A9%E3%83%AB%E7%84%A1%E5%90%8D%E9%96%A2%E6%95%B0%E3%81%AE%E4%BB%A3%E5%85%A5)の「function」部分が無くなり、代わりに「=>」が使われているのがわかる。 ####アロー関数を変数に代入するやり方 [関数の構文](https://qiita.com/Stack_up_Rising/items/486de1a2374785d6a10e#%E9%96%A2%E6%95%B0%E3%81%AE%E6%A7%8B%E6%96%87)で書いた「アロー関数を変数に代入する」やり方で例題を書いていく。
アロー関数を変数に代入
index.js
let averageHeight = (a, b, c) => {
  let totalHeight = a + b + c;
  return totalHeight / 3;
};
console.log(averageHeight(160, 165, 170));

【実行結果】

165
#####例題に関してのアンサー これは3つの身長の平均を求めている。 戻り値や引数の使い方は通常の関数で扱う時と変わりない。 また、条件によってはさらに簡略化することができる。
####引数がない場合のアロー関数の書き方
引数がない場合のアロー関数
index.js
let greeting = () => {
  console.log('Hello, World!')
};
greeting();

【実行結果】

Hello, World!
引数がない場合は、「()」の中身を空白のままにする。 注意点として、「()」を省略することは出来ない
####引数がひとつだけの場合
引数がひとつだけ
index.js
let greeting = (name) => {
	console.log('Hello,' + name);
};
greeting('K!');

【実行結果】

Hello, K!
引数がひとつだけの場合は、「()」を省略することができる。 このように引数の「name」を「()」で囲む必要は無い
####処理文が一行の場合 アロー関数の処理文が戻り値を使った一行だけの場合は、 「{}」と「return」を省略して書くことが出来る。 下記の条件を基に、省略する場合とそうでない場合の違いを記載する。

変数totalHeightに対し引数a,bを渡す。

コンソールへ出力する際に変数にそれぞれ値を渡したものをそれぞれの引数に返す。


#####省略しない場合
省略しない場合
index.js
let totalHeight = (a, b) => { return a + b; };
console.log(totalHeight(160, 170));

【実行結果】

330

#####省略する場合
省略する場合
index.js
let totalHeight = (a, b) => a + b;
console.log(totalHeight(160, 170));

【実行結果】

330
結果は同じだが、省略する場合の方が見た目がスッキリとする印象になる。 ##3. アロー関数と通常の関数の違い アロー関数は既存の関数定義を簡略化するために導入されたため、 「書き方が短くなっただけ」と思われがちだが、 通常の関数と別の挙動を起こすという特徴がある。 ###thisの固定化 アロー関数の一番の特徴は、通常の関数と「this」の扱いが異なる点である。 ###thisって何よ? :::note 一般的にJavaScriptでは、関数の中でthisを使うとその呼び出し元のオブジェクトになる。 ::: :::note 読み取り専用のグローバル変数のようなものであり、どこからでも参照が可能。 ::: :::note 通常の関数では呼び出し元のオブジェクトを参照し、 またスコープ※や状況によって、thisの意味も変化する。 :::
スコープとは、

影響範囲のこと。
(例)「変数のスコープ」と出てきたら「その変数が使える範囲」…など。

このサイトでわかりやすく解説されている。


しかしアロー関数では、アロー関数が定義された段階でthisが固定化される。 つまり、アロー関数のthisが定義時に設定された対象のまま変化することがないということ。 言葉だけではイメージしづらいので、例を挙げて解説していく。
####thisを使ったアロー関数の例 2つの例を挙げる。 例の内容は、無名関数とアロー関数でthisを使用する。 #####無名関数
無名関数のコード
index.js
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


#####アロー関数
アロー関数のコード
index.js
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
こちらは無名関数を使ったthisと違い、実行結果がすべてredとなった。 #####なぜそうなったのか? アロー関数では、関数が宣言された時点でthisが確定されるから。 また、その都度thisの内容を変化させたい場合はアロー関数の使用を控えた方がよい。 出力結果を1つに絞るのであれば、アロー関数を使用するという選択肢を持った方がいいと思う。
###コンストラクタを持たない ####コンストラクタとは? :::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)で挙げたように無名関数とアロー関数で例題を挙げていく。 ####無名関数を使ったコンストラクタの例
無名関数を使ったコンストラクタ
index.js
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'
####アロー関数を使ったコンストラクタの例
アロー関数を使ったコンストラクタ
index.js
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
index.js
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の例
アロー関数を使ったaruguments
index.js
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'
こちらも[コンストラクタの例](https://qiita.com/Stack_up_Rising/items/486de1a2374785d6a10e#%E3%82%A2%E3%83%AD%E3%83%BC%E9%96%A2%E6%95%B0%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%E3%81%AE%E4%BE%8B)と同様に、アロー関数ではargumentsを生成することが出来ない。 ##4. JavaScriptにおけるクラスの定義 オブジェクト指向と呼ばれるプログラミング言語についての概念がある。 ###オブジェクト指向とは? :::note プログラムを現実のモノのように構成する指向のこと。 ::: このオブジェクト指向の言語として作られたプログラミング言語には、 多くの場合クラスという機能が備わっている。
###クラスとは? :::note 端的にいえばモノの設計書のこと。 :::

HTMLのclassとはまったくの別物である。

先ほどオブジェクト指向とは、プログラミングを現実のモノのように構成するもの
だと解説したが、クラスとはこのモノのように構造する際のモノそのものの設計書にあたる。
言葉としては、以下のような意味の対応表になる。

◇オブジェクト = モノ
◇クラス = モノ(オブジェクト)の設計書


###クラスの特徴 JavaScriptにおけるクラスの書き方は特殊な書き方をする。以下は一例。

クラス変数はコンストラクタの中でのみ記載できる

クラス直下にはfunction(メソッド、関数)のみしか配置できない

など。次は使い方を説明する。


###クラスの作り方 ####クラス宣言 クラス宣言によるクラスの定義は以下のように行う。
クラス宣言によるクラスの定義
index.js
class Fooclass {
  // ...クラスの内容をここで定義する
}

関数宣言時に発生するホイスティング(巻き上げ)は、クラス宣言時には発生しない。

ホイスティング(巻き上げ)とは?

コンテキスト内で宣言した変数や関数の定義をコード実行前にメモリに配置すること

ホイスティングのことを「宣言の巻き上げ」といったりもする。

変数が宣言される前に、その変数をアクセス可能にしたり利用可能にしたりすること

例を挙げると、

index.js
a();
function a() {
  console.log('a');
}

関数の前で呼び出しても実行できるのは、コード実行の前に関数がすでにメモリ上に存在するためであり、
つまりホイスティングがあるため。

クラスを使用したい場合は必ずクラスを使用する前に、クラス宣言を行うことが必要となる。


####クラス式 クラス式によるクラスの定義は以下のように行う。
クラス式によるクラスの定義
index.js
const fooclass = class {
  // ...クラスの内容をここで定義する
};

クラス式の場合、その名の通り、式としてクラスを定義することが出来る。

変数に代入するclass式は無名クラスでも名前つきクラスでも可能。

####constructorとメソッド定義 クラス宣言やクラス式でクラスを定義する際、そのクラスの中身が どのようなものなのかについて、JavaScriptでは主に コンストラクタ(constructor)メソッド で定義していく。 #####constructor
constructorで定義
index.js
const fooclass = class {
  constructor(x, y) {
  // コンストラクタ
    this.x = x;
    this.y = y;
  }
};

constructor()という名前のメソッドがコンストラクタと呼ばれるものである。

コンストラクタは定義したクラスからオブジェクトを生成し、初期化する際に実行される特殊な初期化用メソッドである。

constructor()内でthis.xにconstructor()の第一引数xを代入しているが、これは
自身のプロパティxに指定された引数の値を設定する
という意味になる。(第二引数yも同様)

#####メソッド定義 コンストラクタとは別に通常のメソッドもクラスの内容として定義することが可能。 コンストラクタの他にメソッドも定義する場合、以下のようなコードになる。
メソッドでの定義
index.js
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演算子を用いる必要がある。
オブジェクトの作成
index.js
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とかタヒぬほど使うのでしっかり覚えていきたい
0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?