JavaScript | POPWEB https://blog.popweb.dev Developer, Blogger, Illustrator in Tokyo. Mon, 17 Jul 2023 13:12:03 +0000 ja hourly 1 https://wordpress.org/?v=6.7.6 https://storage.googleapis.com/gcs.pop-web.net/2021/01/favicon.ico JavaScript | POPWEB https://blog.popweb.dev 32 32 window.top === window.selfは何をしているのか? https://blog.popweb.dev/programming/javascript/window-top-window-self/ https://blog.popweb.dev/programming/javascript/window-top-window-self/#respond Tue, 04 Jan 2022 13:48:00 +0000 https://blog.popweb.dev/?p=1745 プロジェクトで「if (window.top === window.self) {…}」という条件式を初めて見たので、その意味を調べてみました。 この条件式は、開いているページがiframeで読み込んだページを表示してい […]

The post window.top === window.selfは何をしているのか? first appeared on POPWEB.]]>
プロジェクトで「if (window.top === window.self) {…}」という条件式を初めて見たので、その意味を調べてみました。

この条件式は、開いているページがiframeで読み込んだページを表示しているかどうかを判定するためのものです。

window.topは、最上位のコンテキスト(ウィンドウ)を返します。iframe要素を複数埋め込んでいる場合でも、一番上の親ウィンドウを取得することができます。

一方、window.selfは現在のウィンドウ自身を返します。

したがって、iframeがないページではwindow.selfwindow.topは等価になります。これを利用して、ページがiframeを含むかどうかを判定することができます。

The post window.top === window.selfは何をしているのか? first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/window-top-window-self/feed/ 0
【JavaScript】プリミティブ型(基本型)とオブジェクト型(参照型)について https://blog.popweb.dev/programming/javascript/primitive-object/ https://blog.popweb.dev/programming/javascript/primitive-object/#respond Sat, 16 Oct 2021 08:14:10 +0000 https://blog.pop-web.net/?p=1020 JavaScriptにのデータ型には、プリミティブ型(基本型)とオブジェクト型(参照型)がある 目次 プリミティブ型(基本型)オブジェクト型(参照型)プリミティブ型とオブジェクト型の違いプリミティブ型の場合オブジェクト型 […]

The post 【JavaScript】プリミティブ型(基本型)とオブジェクト型(参照型)について first appeared on POPWEB.]]>
JavaScriptにのデータ型には、プリミティブ型(基本型)とオブジェクト型(参照型)がある

プリミティブ型(基本型)

真偽値(boolean)
数値(number)
文字列(string)
null
undefined
シンボル(symbol)

オブジェクト型(参照型)

プリミティブ型(基本型)以外のデータ型

オブジェクト(object)
配列(array)
関数(function)

プリミティブ型とオブジェクト型の違い

プリミティブ型は変数をコピーする時、変数の中身の値がそのままコピーされる。

オブジェクト型は変数をコピーする時、その値がコピーされるのではなく、そのオブジェクトが持っているメモリ上のアドレスがコピーされる。

以下がその例

プリミティブ型の場合

let a = 20;
let b = a;
a = 80;
console.log(a); // 80
console.log(b); // 20

bにaを代入した後にaに新たに100の値を代入しているが、bに影響は受けない。

さっきも説明したように、メモリ参照せずに変数の値をそのままコピーしていることが分かる。

オブジェクト型の場合

let obj1 = {
  name: "ken",
  age: 20
};
let obj2 = obj1;
obj1.age = 80;
console.log(obj1.age); // 100
console.log(obj2.age); // 100

obj2にobj1を代入することで、obj1もobj2も同じメモリ上のアドレスと保持するようになったため、片方のageの値を変更すると、もう片方の値も同じになる。

The post 【JavaScript】プリミティブ型(基本型)とオブジェクト型(参照型)について first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/primitive-object/feed/ 0
eslintのルールで特定ファイルのみ適用外にしたい場合の書き方 https://blog.popweb.dev/programming/javascript/eslint-overrides/ https://blog.popweb.dev/programming/javascript/eslint-overrides/#respond Fri, 28 May 2021 08:14:55 +0000 https://blog.popweb.dev/?p=1697 .eslintrcのrulesに「defalut export」の記述を禁止するルールを追加したとする。 このとき、sotrybookのstories.tsxにはこのルールは適用外にしたい。 その場合にはどうするか? o […]

The post eslintのルールで特定ファイルのみ適用外にしたい場合の書き方 first appeared on POPWEB.]]>
.eslintrcのrulesに「defalut export」の記述を禁止するルールを追加したとする。

module.exports = {
...
  rules: {
...
    "import/no-default-export": "error",
...
  }
};

このとき、sotrybookのstories.tsxにはこのルールは適用外にしたい。

その場合にはどうするか?

overridesを使って、適用したいファイル名とルールを無効化する記述すればよい。

module.exports = {
...
  rules: {
...
    "import/no-default-export": "error",
...
  },
  overrides: [
    {
      files: ["**/*.stories.tsx"],
      rules: {
        "import/no-default-export": "off",
      },
    },
  ],
};

参考文献

Configuration Files - ESLint - Pluggable JavaScript Linter
A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code qua...
The post eslintのルールで特定ファイルのみ適用外にしたい場合の書き方 first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/eslint-overrides/feed/ 0
【JavaScript入門】reduceについて説明と使い方 https://blog.popweb.dev/programming/javascript/javascript-reduce/ https://blog.popweb.dev/programming/javascript/javascript-reduce/#respond Tue, 26 Jan 2021 09:06:52 +0000 https://blog.popweb.dev/?p=1476 reduce()の説明と使い方を説明する。 reduce()は、配列の隣り合う値に対して関数を適用させて、その実行結果を返すメソッド。 コードを書いて動きを確認してみる。 まず、第2引数のcurには、arrの値が順番に入 […]

The post 【JavaScript入門】reduceについて説明と使い方 first appeared on POPWEB.]]>
reduce()の説明と使い方を説明する。

reduce()は、配列の隣り合う値に対して関数を適用させて、その実行結果を返すメソッド。

コードを書いて動きを確認してみる。

const arr = [1, 2, 3]

console.log(arr.reduce((acc, cur) => acc + cur)) // 6

まず、第2引数のcurには、arrの値が順番に入ってくる。

第1引数accには前回実行時の値が入ってくる。

そして、返値として最後に実行された値を返す。

具体的に実行処理は以下。

  • 1回目の実行:cur=1となり、前回実行の値はないので、1が返る
  • 2回目の実行:cur=2となり、前回実行の値はacc=1なので、1+2=3が返る
  • 3回目の実行:cur=3となり、前回実行の値はacc=1なので、3+3=6が返る
The post 【JavaScript入門】reduceについて説明と使い方 first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/javascript-reduce/feed/ 0
【コンソールエラーの原因】front.js:8 Uncaught ReferenceError: browser is not defined at HTMLDocument. https://blog.popweb.dev/programming/javascript/browser-is-not-defined-at-htmldocument/ https://blog.popweb.dev/programming/javascript/browser-is-not-defined-at-htmldocument/#respond Fri, 29 May 2020 14:20:53 +0000 https://blog.pop-web.net/?p=1283 Google Chromeのデバッグコンソールに以下のようなエラーが出た。 色々調べた結果、今回の場合はGoogle Chromeの拡張機能が原因だった。 シークレットウィンドウで再度表示してみると、エラーはでない。 ど […]

The post 【コンソールエラーの原因】front.js:8 Uncaught ReferenceError: browser is not defined at HTMLDocument. first appeared on POPWEB.]]>
Google Chromeのデバッグコンソールに以下のようなエラーが出た。

front.js:8 Uncaught ReferenceError: browser is not defined at HTMLDocument.<anonymous> (front.js:8)

色々調べた結果、今回の場合はGoogle Chromeの拡張機能が原因だった。

シークレットウィンドウで再度表示してみると、エラーはでない。

どの拡張機能が原因でエラーが出ているかは各自で調べて頂きたい。

結論→あまり気にすることではなかった。

The post 【コンソールエラーの原因】front.js:8 Uncaught ReferenceError: browser is not defined at HTMLDocument. first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/browser-is-not-defined-at-htmldocument/feed/ 0
JavaScript(ES2015)アロー関数の省略の書き方とthisの束縛について https://blog.popweb.dev/programming/javascript/arrow-function/ https://blog.popweb.dev/programming/javascript/arrow-function/#respond Sat, 09 May 2020 11:07:00 +0000 https://blog.pop-web.net/?p=1230 ES2015(ES6)から追加されたアロー関数だが、省略した書き方が色々あるのでまとめてみる。 目次 アロー関数の基本形普通の無名関数の記述アロー関数を使った無名関数の記述アロー関数の省略系引数が1つの時 引数の() を […]

The post JavaScript(ES2015)アロー関数の省略の書き方とthisの束縛について first appeared on POPWEB.]]>
ES2015(ES6)から追加されたアロー関数だが、省略した書き方が色々あるのでまとめてみる。

アロー関数の基本形

アロー関数は基本的には、無名関数(匿名関数)の省略記法で、その名の通り=>を使って関数リテラルを定義する。

まずは基本形から。

普通の無名関数の記述

function(params) {
  // 処理の内容...
}

アロー関数を使った無名関数の記述

(params) => {
  // 処理の内容...
}

アロー関数の省略系

引数が1つの時 引数の() を省略できる

params => {
  // 処理の内容...
}

引数がない時は、() を書く必要がある。

() => {
  // 処理の内容...
}

処理の内容が1つだけの場合、{} を省略できる

params => returen value

値を返すだけの時、return を省略できる

params => value

オブジェクトを返す時は() で囲む必要がある

オブジェクトリテラルを返す時は、{} が関数ブロックと解釈されてしまうため () で囲む必要がある。

const f = param => ({foo: "bar"})
console.log(f()) // { foo: "bar" }

() で囲むルールが逆に見づらい場合は、素直に return でオブジェクトリテラルを返せばよい。

const f = param => { return { foo: "bar" } }
console.log(f()) // { foo: "bar" }

アロー関数におけるthisの束縛について

アロー関数内のthisと従来の無名関数の中のthisは違うところを指していることに注意する。

以下のように、コードがあるとする。

function Count() {
  this.count = 0 // 注①
  setInterval(function() {
    this.count++ // 注②
  }, 1000)

}

const counter = new Count()

// 3秒後に呼び出すので「3」と出力されるはずだが…
setTimeout(() => console.log(counter.count), 3000) 

Count()は、1秒ごとに+1するコンストラクタである。

それを3秒後に呼び出すので「3」と出力されるはずだが、実際は「0」のままになる。

注①のthisは、Count()インスタタンスのthisとして定義されるが、注②のthisは、グローバルオブジェクトのthisとして定義されるため、注①のthis.countはずっと「0」のままになる。

これを回避するための方法としてよく用いいられるのが、var self = thisというコードを書いて呼び出し先のthisを制御する。

function Count() {
  this.count = 0
  var self = this // 一旦、インスタタンスのthisをselfに入れる。
  setInterval(function() {
    self.count++ // selfに変え、呼び出し元をインスタンスの方を参照するようにする。
  }, 1000)

}

const counter = new Count()
// 期待通り「3」と出力される。
setTimeout(() => console.log(counter.count), 3000)

ここで、アロー関数に書き方を変更すると、var self = thisは必要なくなる。

なぜなら、アロー関数だとthisを束縛できるからである。

「thisを束縛」とは何か?

従来のアロー関数ではない中のthisは関数が呼び出された時点でthisが決まっていく。

つまり、thisが確定していない。thisを束縛していないことになる。

上のソースの例だとsetIntervalのコールバック関数として関数が実行されるためthisはクローバルオブジェクトのthisを指すことになる。

アロー関数の場合、アロー関数内のthisは定義されて時点で確定(=束縛)する。

そのため、アロー関数内のthisはCount()インスタタンスを指すことになる。

先ほどコードは、以下のようにアロー関数へ書き方を変えることで期待の動作をしてくれる。

function Count() {
  this.count = 0
  setInterval(() => {
    this.count++ // インスタンスのthisを参照する。
  }, 1000)

}

const counter = new Count()
setTimeout(() => console.log(counter.count), 3000)

以上のように、従来の無名関数の書き方より、アロー関数を使った方がthisの扱いが楽になり、混乱することがなくなるので基本的にはアロー関数を使うよう慣れた方良い。

The post JavaScript(ES2015)アロー関数の省略の書き方とthisの束縛について first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/arrow-function/feed/ 0
npm installしたがキャッシュとパッケージ(node_modules)を削除して再インストールする方法 https://blog.popweb.dev/programming/javascript/npm-install-cache-clean/ https://blog.popweb.dev/programming/javascript/npm-install-cache-clean/#respond Fri, 01 Nov 2019 02:08:00 +0000 https://blog.pop-web.net/?p=592 npm installでパッケージをインストールしたが、再インストールして入れなおすには以下のようにする。

The post npm installしたがキャッシュとパッケージ(node_modules)を削除して再インストールする方法 first appeared on POPWEB.]]>
npm installでパッケージをインストールしたが、再インストールして入れなおすには以下のようにする。

npm cache clean
rm -rf node_modules/
npm install
The post npm installしたがキャッシュとパッケージ(node_modules)を削除して再インストールする方法 first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/npm-install-cache-clean/feed/ 0
【JS初心者向け】async/awaitのついて解説 https://blog.popweb.dev/programming/javascript/async-await/ https://blog.popweb.dev/programming/javascript/async-await/#respond Fri, 13 Sep 2019 18:39:20 +0000 https://blog.pop-web.net/?p=559 前回記事のPromiseについて解説の続きで、今回はasync/awaitについて解説していく。 async/awaitを使うことで、前回に解説したPromiseの処理を同期的な記述で簡潔に書けるようになる。 目次 as […]

The post 【JS初心者向け】async/awaitのついて解説 first appeared on POPWEB.]]>
前回記事のPromiseについて解説の続きで、今回はasync/awaitについて解説していく。

async/awaitを使うことで、前回に解説したPromiseの処理を同期的な記述で簡潔に書けるようになる。

asyncとawaitと使う

Promiseインスタンスを返してくれるgetHello( )がある。前回のPromiseではこの関数にthenメソッドを繋いであげれば、非同期処理後の処理の記述ができた。

まず、このthenメソッドを同じ役割をしてくれるのがawaitである。

await getHello( )とすることでgetHello( )の非同期処理が終わるまで、その後に記述される処理を実行せずに待ってくれる。

非同期処理が完了すれば、hello = await getHello('Start!')としているので、Promiseでラップされた値が取り出され変数helloに代入される。

非同期処理が完了後、この後に記述される処理が順次実行されるという流れになる。

awaitを内部で使っている関数には、先頭にasyncをつけるルールになっているので忘れずにつけておく。

function getHello(msg) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(msg)
      resolve("Hello,World!")
    }, 2000)
  })
}

async function func() {
  let hello
  hello = await getHello('Start!')
  console.log(hello)
  hello = await getHello('Start!')
  console.log(hello)
}

func()

上記のコード実行すると、2秒後ごとにコンソールへ文字が表示されることになる。

例外処理の方法

async/awaitの場合、try/catchブロックで例外処理を行う。

function getHello(msg) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(msg)
      resolve("Hello,World!")
    }, 2000)
  })
}

async function func() {
  let hello
  try {
    hello = await getHello("Start!")
    console.log(hello)
  } catch (e) {
    throw new Error("Error!", e)
  }

  return hello
}

func().then(()=>{
console.log('ttt')
})

また、留意しておきたい点として、asyncの中でreturnされた値はPromiseでラップされた状態で返却されるということ。

上のコードでconsole.log(func())で確認すると、Promiseインスタンスが返ってきていることがわかる。

console.log(func()) // Promiseインスタンスが返ってきていることを確認
画像

このように、thenメソッドを使っていた時より、分かりやすく非同期処理を書けるようになる。

async/awaitをつかうと、あたかも非同期処理を同期的に記述できるのがメリットである。

The post 【JS初心者向け】async/awaitのついて解説 first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/async-await/feed/ 0
【JS初心者向け】Promiseについて解説 https://blog.popweb.dev/programming/javascript/js-promise/ https://blog.popweb.dev/programming/javascript/js-promise/#respond Fri, 06 Sep 2019 11:57:31 +0000 https://blog.pop-web.net/?p=541 前回の「コールバック関数について解説」の続き。 非同期処理の際にコールバック関数を使うのは有効であるが、コールバック地獄に陥りやすいデメリットがある。 コールバック地獄対策として非同期処理を簡潔に書けるようにしたのが、P […]

The post 【JS初心者向け】Promiseについて解説 first appeared on POPWEB.]]>
前回の「コールバック関数について解説」の続き。

非同期処理の際にコールバック関数を使うのは有効であるが、コールバック地獄に陥りやすいデメリットがある。

コールバック地獄対策として非同期処理を簡潔に書けるようにしたのが、Promiseである。

Promiseについて

Promiseについて簡単にいうと、非同期処理の処理状態を示してくれるやつだ。

非同期処理においてポイントなることは、非同期処理が終わった後のタイミングで、違う処理を処理をしたいということ。

そうなると、非同期処理の状態が完了しているのか?まだ終わってなのか?が知る必要がある。

そこで、Promiseの登場。

Promiseの引数に非同期処理を入れてやり、その戻り値を取得することでで状態を判別できる。

Promiseの使い方

Promiseの使い方は、return new Promise( )の引数としてコールバック関数を入れる。

このコールバック関数に、非同期処理の内容を記述する。

以下の例では、setTimeout( )関数を使って非同期処理を記述した。

function getHello(msg) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(msg)
      resolve("Hello,World!")
    }, 2000)
  })
}

getHello('Start!').then(msg => {
  return getHello(msg)
})

まず、関数であるgetHello( )を実行すると戻り値(return)としてPromiseクラスのインスタンスが返ってくる。

このPromiseインスタンスに、非同期処理の処理状態の情報が入っている。

console.log(getHello( ))で確認すると、確かにPromiseが返ってきていることがわかる。

console.log(getHello()) // Promiseインスタンスが返ってきていることを確認

Promiseのコールバック関数の第一引数としてresolve、第二引数としてrejectを設定することができ、処理が成功した時はresolve、失敗した時はrejectが呼び出される。

コールバック関数の処理の最後にresolve又は、rejectメソッドを呼び出すことで処理の終了を明示的に表す。

次に、Promiseクラスのインスタンスはthenメソッドを持っており、引数にコールバック関数を入れてあげる。

thenメソッドの第一引数には、resolve又は、rejectメソッドで渡された値が入る。

つまり、Promiseで処理した後の値をthenメソッドに渡して処理をさせるということができる。

また、thenメソッドの中でreturnで再度Promiseインスタンスを返すことで、非同期処理を実行することができる。

上のコードでは、resolveメソッドで処理が終了して、thenメソッドで渡された値が処理されるという流れになり、2秒後に「Start!」、またその2秒後に「Hello,World!」と表示される。

ポイントとしては、thenメソッドで値をPromiseインスタンスをreturnさせると、また次のthenメソッドのコールバック関数の第一引数として受け取ることができる。

上で使ったコードを書き直す。

以下、getByebye( )という関数を追加して、thenメソッドを以下の方に追加して繋げる。

function getHello(msg) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(msg)
      resolve("Hello,World!")
    }, 2000)
  })
}

function getByebye() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Bye!Bye!")
    }, 3000)
  })
}

getHello('Start!').then(msg => {
  return getHello(msg)
}).then(() => {
  return getByebye()
}).then(msg => {
  console.log(msg)
})

このコードを実行すると、2秒後に「Start!」、またその2秒後に「Hello,World!」と表示され、さらにその3秒後に「Bye!Bye!」と表示される。

以下のように、thenメソッドの引数としてgetByebyeをそのまま渡してあげても同じになる。

getHello('Start!').then(msg => {
  return getHello(msg)
}).then(getByebye)
.then(msg => {
  console.log(msg)
})

エラー処理のrejectについて

Promiseの第二引数であるrejectはエラー処理を扱うもので、値をcatchメソッドで受け取る。

さっきほどのgetHello関数のresolveで返却していたところをrejectに変更する。

function getHello(msg) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(msg)
      reject("Error!")
    }, 2000)
  })
}

function getByebye() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Bye!Bye!")
    }, 3000)
  })
}

getHello('Start!').then(msg => {
  return getHello(msg)
}).then(() => {
  return getByebye()
}).then(msg => {
  console.log(msg)
}).catch(error => {
  console.log(error)
})

これを実行すると、getHello関数以降の処理が飛ばされて、「Error!」という表示される。

Promise.allで配列で処理

Promise.allメッドを使うことで関数を配列として渡し、並列処理することができる。

以下のよう、返却される値はresolveの値が配列として返ってくる。

function getHello(msg) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(msg)
      resolve("Hello,World!")
    }, 2000)
  })
}

function getByebye() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Bye!Bye!")
    }, 5000)
  })
}

Promise.all([
    getHello('Start!'),
    getByebye()
  ])
  .then((msgs) => {
    console.log(msgs)
  })

上記のコードを実行すると、2秒と5秒で秒数は異なるが、すべてが完了するまでthenメソッドは実行されない。

以下のように、5秒後にresolveの値がすべて入った配列が返される。

省略して書くことも可能

Promiseの以下のコードは省略して書くことも可能。

function getHello() {
    return new Promise((resolve, reject) => {
        resolve("Hello,World!")
    })
}

省略形は以下のよう。

function getHello() {
    return Promise.resolve("Hello,World!")
}

次の記事では、Promiseを同期処理ように書くことができるasync/awaitについて解説していく。

async/awaitについては下記↓

The post 【JS初心者向け】Promiseについて解説 first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/js-promise/feed/ 0
【JS初心者向け】コールバック関数について解説 https://blog.popweb.dev/programming/javascript/js-callback/ https://blog.popweb.dev/programming/javascript/js-callback/#respond Thu, 05 Sep 2019 11:09:10 +0000 https://blog.pop-web.net/?p=539 JavaScriptをやっていて、「async/await」ってなんやって思って、ググったら「Promise」の進化版ってことらしい。 今度は「Promise」をググったら、コールバック関数の概念が必要ってことが分かった […]

The post 【JS初心者向け】コールバック関数について解説 first appeared on POPWEB.]]>
JavaScriptをやっていて、「async/await」ってなんやって思って、ググったら「Promise」の進化版ってことらしい。

今度は「Promise」をググったら、コールバック関数の概念が必要ってことが分かった。

とういうことで、コールバック関数とコールバック関数の必要性について説明する。

コールバック関数について

コールバック関数とは、「引数として渡される関数」こと。

そして、渡された先で関数を実行処理する。

あの処理が終わったタイミングで、この関数を実行したい」といった場面で使われる。

まず、setTimeout関数を使って簡単にコールバック関数について説明する。

以下のように、setTimeoutという関数の引数として関数を渡して、setTimeout内で実行させている。

setTimeout(() => {
  console.log('Hello,world!')
}, 1000);

ここで、コールバック関数は、「() => { console.log(‘Hello,world!’)}」。

コールバック関数を受け取る側の関数は、setTimeout()ということになり、この「コールバック関数を受け取る側の関数」は、「高階関数」と呼ばれる。

JavaScriptでは「関数」も「値」

関数の引数へ、違う関数を渡すことができると説明した。

これはJavaScriptでは、関数を値として扱うことができるからだ。

どういうことかというと、関数を変数に入れることができるということ。

関数を変数に入れるには、関数名の後ろにカッコをつけない。

以下のよう。

function hello(){
  console.log('Hello,world!')
}

// 関数を変数へ代入するときは、カッコをつけない
const helloFunc = hello

// 関数を呼び出す場合はカッコをつける
helloFunc() // Hello,world!とコンソールに表示
hello() // Hello,world!とコンソールに表示

関数に関数を渡して実行することで順序を制御

関数は値なので、関数の引数へ渡すことができる。

そして、引数で渡した関数を渡した関数の中で実行ができる。

さっきのコードにbye()とう関数を追加して、それをhello()関数の引数として渡す。

そして、渡した関数を渡された関数の中で実行する。

function hello(func){
  console.log('Hello,world!')
  func()
}

function bye(){
  console.log('Bye!Bye!')
}

hello(bye)
// 表示結果
// Hello,world!
// Bye!Bye!

みてわかる通り、hello()関数の内容が実行された後からではないと、bye()関数が処理されないようになっており、実行順序を制御している。

このように、「あの関数処理の後に、この関数を実行する」といった処理にしたいときに使うのがコールバックだ。

この場合、bye()がコールバック関数、hello()が高階関数になる。

前述したように、高階関数はコーバック関数を受け取る側の関数のこと。

また、bye()の内容をそのまま、hello()の引数に入れて、以下のように書くこともできる。

function hello(func){
  console.log('Hello,world!')
  func()
}

hello(function(){
  console.log('Bye!Bye!')
})
// 表示結果
// Hello,world!
// Bye!Bye!

さらに、渡した関数に値を渡して実行もできる。

function hello(func){
  console.log('Hello,world!')
  func('Bye!Bye!')
}

hello(function(msg){
  console.log(msg)
})
// 表示結果
// Hello,world!
// Bye!Bye!

ちなみに、この場合だとfunction(msg)がコールバック関数、hello()が高階関数になる。

コールバック関数の必要性と使い所について

JavaScriptでコールバック関数の使い所はというと、「非同期処理」と組み合わせて使われる。

まず、JavaScriptは非同期処理を前提として設計されたスクリプトであることを理解しておく。

非同期処理では、実行順序がコードの通りにならない場合がある。

例えば、APIを呼び出してそのデータを扱いたい場合、APIから渡してもらうデータを全て読み込んだ後に処理を実行しなければ、データを扱うことはできなくなる。

つまり、実行順序を制御する必要がある。

そのため、コールバック関数で制御処理するという方法が用いられる。

次で例を出す。

コールバック関数と非同期処理の組み合わせ例

非同期処理をする関数であるsetTimeoutを使って、コールバック関数と非同期処理の組み合わせ例と出す。

まずは、コールバック関数なしだとうまくいかない例を出す。

うまくいかない例(コールバック関数なし)

function getHello(){
  setTimeout(function(){
    return 'Hello,World!'
  },2000)
}

const hello = getHello()
console.log(hello) // undefinedになってしまう

これの例だと、getHello()の中の処理が、2秒後にしか実行されないので、console.log(hello)は、undefinedになる。

うまくいく例(コールバック関数あり)

先ほどのうまくいかない例に、コールバック関数をつかって書き直す。

function getHello(callback){
  setTimeout(function(){
    callback('Hello,World!')
  },2000)
}

getHello(function(msg){
  console.log(msg)
})

まず、function(msg){console.log(msg)}というコールバック関数を作成して、getHello関数の引数として渡してあげる。

そして、setTimeoutの中でコールバック関数を実行させてあげることで、2秒後に「Hello,World!」と表示されるようになる。

つまり、非同期処理をする関数でも、コールバック関数を使えば、実行順序を思い通りに実行することが可能になる。

しかし、コールバック関数の処理が増えていくと、見た目の構造がどんどん複雑化していく傾向にある。

この複雑化をコールバック地獄という。

次に、コールバック地獄とはなにかについて説明する。

コールバック地獄とは

コールバック地獄について、簡単に説明する。

以下のように、2秒おきにメッセージを上から順番に出したい場合。

  • こんにちは、太郎。
  • やぁ、こんにちは、花子。
  • いい天気だね。
  • 今日はどこに行こうか。
  • そうだね。とりあえずスタバに行こう。

これを2秒ごとに出してくとなると以下のようなコードになる。

function func_01(callback) {
  setTimeout(function () {
    console.log("こんにちは、太郎。");
    callback();
  }, 2000);
}

function func_02(callback) {
  setTimeout(function () {
    console.log("やぁ、こんにちは、花子。");
    callback();
  }, 2000);
}

function func_03(callback) {
  setTimeout(function () {
    console.log("いい天気だね。");
    callback();
  }, 2000);
}

function func_04(callback) {
  setTimeout(function () {
    console.log("今日はどこに行こうか。");
    callback();
  }, 2000);
}

function func_05() {
  setTimeout(function () {
    console.log("そうだね。とりあえずスタバに行こう。");
  }, 2000);
}

func_01(function () {
  func_02(function () {
    func_03(function () {
      func_04(function () {
        func_05();
      });
    });
  });
});

最後の呼び出している関数が入れ子の入れ子の…状態になっている。

とても分かりにくくする。

これを俗に、コールバック地獄という。

そして、コールバック地獄対策のために登場したのが「Promise」で、非同期処理を簡潔に書くことが可能になる。

Promiseについては下記↓

The post 【JS初心者向け】コールバック関数について解説 first appeared on POPWEB.]]>
https://blog.popweb.dev/programming/javascript/js-callback/feed/ 0