🐡

bind(this)って何?よく分からず使ってたので、学び直してみた

に公開

bind が何か、this とどんな関係があるのか、よく分からずに使っていました。
いつ使う必要があるのか、いつ使う必要が無いのかを理解したかったので
調べてみると、

  • thisがなにを指しているのか
  • bindはどのような効果があるのか
    が、見えてきたので記事にまとめてみました!

この記事を書こうと思った理由

  • bind(this)をなんとなく使っている方に向けての知見共有のため
  • 自身の理解を深めるため

ゴール

  • bind(this)を理解して使える

対象者

  • JavaScriptの初学者
  • bind(this)をよく分からず使っている方

bind 理解のために JavaScript の this について理解する

サンプルデータ

thisの理解のために、swiper を使ったサンプルを使います。
メインとなるコードは、下記で、ブラウザの幅を 768px 以下にするとスライダーが有効になります!

mqSp.addEventListener('change', this.handleMediaQuery.bind(this));

下記のリンク、もしくは添付したコードより実際の挙動を確認できます。

https://github.com/d2cSakai/bind-practice

コードサンプル

html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Swiper Slider Example</title>
    <link rel="stylesheet" href="./assets/css/style.css" />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css"
    />
  </head>
  <body>
    <div class="container">
      <h1>Swiper Slider Example</h1>
      <p>ブラウザの幅を768px以下にするとスライダーが有効になります。</p>
      <div class="swiper" data-swiper-container>
        <div class="swiper-wrapper">
          <div class="swiper-slide">
            <img src="./assets/img/flower01.jpg" alt="" />
          </div>
          <div class="swiper-slide">
            <img src="./assets/img/flower02.jpg" alt="" />
          </div>
          <div class="swiper-slide">
            <img src="./assets/img/flower03.jpg" alt="" />
          </div>
        </div>
        <div class="swiper-pagination" data-swiper-pagination></div>
      </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
    <script src="./assets/js/index.js"></script>
  </body>
</html>
css
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.container {
  font-family: sans-serif;
  margin: 20px;
}

.swiper {
  width: 100%;
}

.swiper-slide {
  background-color: #f0f0f0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  border-radius: 8px;
}

.swiper-pagination-progressbar {
  margin-top: 20px;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
}
js
const mqSp = window.matchMedia('(max-width: 768px)');

// スライダーを制御するクラス
class Slider {
  constructor() {
    this.selectors = {
      container: 'data-swiper-container',
      pagination: 'data-swiper-pagination',
    };

    this.containerElm = document.querySelector(`[${this.selectors.container}]`);
    this.slider = null;

    // containerElmが存在しない場合は処理を終了
    if (!this.containerElm) return;

    this.init();
  }

  // 初期化処理
  init() {
    this.handleMediaQuery();
    this.addEventListeners();
  }

  // メディアクエリに応じた処理
  handleMediaQuery() {
    if (mqSp.matches) {
      this.createSlider();
    } else {
      this.destroySlider();
    }
  }

  // イベントの登録
  addEventListeners() {
    mqSp.addEventListener('change', this.handleMediaQuery.bind(this));
  }

  // Swiperの初期化
  createSlider() {
    if (this.slider) return;

    this.slider = new Swiper(this.containerElm, {
      allowTouchMove: true,
      spaceBetween: 20,
      slidesPerView: 1.1,
      grabCursor: true,
      pagination: {
        el: `[${this.selectors.pagination}]`,
        type: 'progressbar',
      },
    });
  }

  // Swiperの破棄
  destroySlider() {
    if (!this.slider) return;

    this.slider.destroy(true, true);
    this.slider = null;
  }
}

// DOMContentLoaded後にスライダーを初期化
document.addEventListener('DOMContentLoaded', () => {
  new Slider();
});

this って何。

thisの理解のために。
.bind(this)を取り除いた状態だと、なぜ動作しないのかを調べていきます!

まずthisを調べるため、実際にコンソールで確認してみます。
console.log(this)とすると、class Slider のインスタンスを指していることがわかります!

少しだけ、thisの事がわかりました!


this.handleMediaQuery を確認する

では、this.handleMediaQuery は、Slider インスタンスのhandleMediaQueryメソッドとなるはず。
こちらも、実際にコンソールで確認してみます!

なんと、consoleにエラーが表示されました。
this.createSlider()this.destroySlider()が実行されていないのが分かります。

this.handleMediaQuery=Slider.handleMediaQueryとなって欲しいのが、
MediaQueryList.handleMediaQuery となってしまっている。
つまりイベントを監視する対象 = mqSpthisになっている。
ということになります。

.bind(this)を取り除いた状態だと、なぜ動作しないのか

consoleの通り、そんな関数(メソッド)は存在しないので、動作しません

さらに少しだけ、thisの事がわかりました!!

bind を理解する

bindなしで関数(メソッド)を渡すとthisの意図しない動作になる事を理解した上で、bind の理解に進みます。

下記のように .bind(this) を追加する事で、

.bind(this)で関数(メソッド)と正しい this(Slider のインスタンス)をセットで渡すことができます。

少しだけ、bindの事がわかりました!

番外

mqSp.addEventListener('change', this.handleMediaQuery())は何故動作しないのか

そもそも、bindthis以前に、
下記のコードが動作しない理由が理解できていませんでした。

addEventListener('change', 〇〇)の〇〇は、関数そのものを渡す必要があります。

mqSp.addEventListener('change', this.handleMediaQuery()) は、
関数の実行結果を渡している事になるので、動作しない。となります。

こちらも実際にconsoleで確認したところ


このようにundefinedになります

何で undefined になる??

では、何故 undefined になるのかサンプルで確認します。
JavaScript の関数は、仕様でundefinedを返します。

Hello 関数を実行

// consoleに「おはよう」と出力される。
function Hello() {
  console.log('おはよう');

  // return undefinedの記述がなくても、undefinedを返しています
  return undefined;
}
Hello();

Hello 関数を console で確認

// consoleにundefinedが出力される
function Hello() {
  console.log('おはよう');
}
console.log(Hello());

つまり
mqSp.addEventListener('change', Hello())
mqSp.addEventListener('change', undefined)と同じ結果となり、動作しない。
となります。

少しだけ、JavaScriptの事がわかりました!

最後に

よく分からず、.bind(this)を使っていて調べてみた事で、
JavaScript の周辺知識も理解することができました!
今回勉強した事で、エラーや詰まった時も理解しながらコードを書くことができそうです!
改めて、調べてまとめてみる大切さを理解しました!

https://github.com/d2cSakai/bind-practice

GitHubで編集を提案
株式会社D2C マーケティング&クリエイティブ事業本部

Discussion