JavaScriptのクロージャの使い方を現役エンジニアが解説【初心者向け】

初心者向けにJavaScriptのクロージャの使い方について現役エンジニアが解説しています。クロージャとは関数とその関数を作った場所をまとめたものです。クラスと同じように変数のスコープを切って、独自に扱うことが出来ます。実際にサンプルプログラムでクロージャを作成してみましょう。

TechAcademyマガジンは受講者数No.1のオンラインプログラミングスクールTechAcademy [テックアカデミー]が運営。初心者向けに解説した記事を公開中。現役エンジニアの方はこちらをご覧ください。

監修してくれたメンター

永井浩平

BtoBtoC事業を行っている会社で社内SEとして勤務。
バックエンド、フロント、クラウドなど幅広く業務を行う。
テックアカデミーでは、フロントエンドコース / Javaコースのメンター。

JavaScriptのクロージャの使い方について、テックアカデミーのメンター(現役エンジニア)が実際のコードを使用して初心者向けに解説します。

目次

そもそもJavaScriptについてよく分からないという方は、JavaScriptとは何なのかについて解説した記事を読むとさらに理解が深まります。

 

田島悠介

今回は、JavaScriptに関する内容だね!

大石ゆかり

どういう内容でしょうか?

田島悠介

JavaScriptのクロージャの使い方について詳しく説明していくね!

大石ゆかり

お願いします!

 

はじめに

JavaScirptのクロージャとスコープに関して説明します。

大規模なアプリなどの実装をする際には、複数画面で共通する処理を1つのJavaScriptファイルにまとめることがあります。

複数の処理から変数や関数が呼ばれた際に、今回説明するクロージャやスコープなどが適切に考慮されていない場合、共通変数の値などが前の処理で書き換わって、思わぬ動作不良などを起こす原因となります。

大規模なアプリやプロジェクトでは必要な知識ですので、ぜひ理解して少しずつ日々のコーディングで意識してみてくださいね。

 

グローバルスコープ

グローバルスコープとは、JavaScriptのどこからでもアクセスできる範囲を指します。

 

以下の例ではnameとbirthPlaceがグローバル変数です。

let name  = "";
let birthPlace = "";
const introduce =() =>{
    console.log("名前は" + name + ":出身地は" + birthPlace );
};

 

「名前は田中:出身地は東京」とログに出力したい場合は、以下のように変数に代入してintroduceを呼べば目的の文字列を出力できます。

name = "田中";
birthPlace ="東京";
introduce();     //console => 名前は田中:出身地は東京

上記のように、グローバルスコープはどこからでも参照できるのでとても便利ですが、他の処理からbirthPlaceを変更された場合、「名前は田中:出身地は山梨」というように、誤った結果を出力してしまう可能性があります。

 

以下、外部のAPIからデータを取得する例です。

let globalJsonData;
const getApiData1 = () => {
     //API通信の処理
     fetch('https://XXXXXXXXXXX')
     .then(response => response.json())
     .then( jsonData => globalJsonData =jsonData );
}
const getApiData2 = () => {
     //API通信の処理
     fetch('https://ZZZZZZZZZZZ')
     .then(response => response.json())
     .then( jsonData => globalJsonData =jsonData );
}

getApiData1();
getApiData2();
windows.onload = function(){
    console.log(globalJsonData);  //getApiData1 ,getApiData2 で取得したどちらかのデータが出力
}

非同期通信ですので、getApiData1とgetApiData2、それぞれのAPIの取得のレスポンスの遅い方が、最終的にglobalJsonDataに保持されます。

そのため、ソース上の処理順序としてはgetApiData2の結果が格納されているはずですが、getApiData1の結果となる場合もあります。

 

非同期処理や複数の処理から更新される可能性のあるデータなどは管理が難しくなるため、グローバルスコープで宣言する際には注意が必要です。

globalJsonDataの例では、globalJsonData1,globalJsonData2のように個別に変数を宣言すれば問題ないのですが、nameやbirthPlaceなどの例では人数に比例してどんどん変数が増えていき、現実的ではありません。

そこでクロージャやローカルスコープなどを利用することで、変数やオブジェクトの参照範囲を限定し、誤った参照や更新を防ぐことができます。

 

[PR] フロントエンドで挫折しない学習方法を動画で公開中

クロージャとは

クロージャとは、関数とその関数を作った場所をまとめたものです。

同じ変数をオブジェクトごとに安全に使いまわすことができ、ソース量も減らせます。

 

以下の例を見てみましょう。

let makeHuman = function (parName,parBirthPlace){
    let name = parName;
    let birthPlace =parBirthPlace;
    return {
        introduce:function(a){ console.log("名前は" + name +":出身地は" + birthPlace );},
    }
}
const tanaka = makeHuman('田中','東京');
tanaka.introduce();//①console => 名前は田中:出身地は東京

const yoshida = makeHuman('吉田','山梨');
yoshida.introduce();//②console => 名前は吉田:出身地は山梨

tanaka.introduce();//③console => 名前は田中:出身地は東京

console.log(birthPlace);//× makeHumanオブジェクトのプロパティ「birthPlace」は直接参照出来ないためエラー
                        //Uncaught ReferenceError: birthPlace is not defined

「makeHuman」というオブジェクトには、name,birthPlaceの2つの変数とintroduceという1つの関数が定義されています。

makeHuman(‘名前’,’出身地’);を呼び出しtanakaというオブジェクトに格納しました。

このtanakaがクロージャです。

tanakaのnameはtanakaというオブジェクト内でのみ参照できるので、yoshidaというオブジェクトを宣言しても、tanakaのnameやbirthPlaceは影響を受けません。

また17行目の記載で、makeHumanオブジェクトのbirthPlaceというプロパティを直接参照することもできないのでエラーとなります。

ECサイトの買い物かごなどの情報を一時的にJavaScriptで処理する際には、商品情報などを上記のようなオブジェクトごとに持たせて配列などで管理できます。

 

ローカルスコープとは

グローバルスコープの対となるローカルスコープは参照範囲を限定できます。

また、種類は以下の2つありますので覚えておきましょう。

 

1、関数スコープ

以下の例のように、関数の{}で区切られた範囲を指します。

グローバル変数と違い、関数が別であれば同じ変数名も宣言できます。

const getName = () => {
  let element = document.getElementById('name');
  return element.value;
};

const getBirthPlace = () => {
  let element = document.getElementById('birthPlace');
  return element.value;
};
console.log(getName());        //console => nameに入力された値を出力
console.log(getBirthPlace());  //console => birthPlaceに入力された値を出力
console.log(element);// ×関数スコープ内のオブジェクトを直接参照出来ないためエラー
                     //Uncaught ReferenceError: birthPlace is not defined

 

2、ブロックスコープ

ブロックスコープは{}に囲まれた中だけ有効となります。

以下の例では、1つめのconsole.logでは同一の{}内なのでnameを参照できますが、2つめのconsole.logでは{}の外なのでnameを参照できません。

{
    let name = "田中";
    console.log(name);//console => 田中
}
console.log(name);// ×ブロックスコープ内のオブジェクトを直接参照出来ないためエラー
                     //Uncaught ReferenceError: birthPlace is not defined

 

クロージャでは、関数スコープを利用します。

関数の中で作られた変数や処理は、関数の中でしかアクセスできないということを覚えておいてください。

 

クロージャを使うメリット

関数スコープを使い、変数や処理を閉じ込めて専用にするクロージャのメリットは、グローバルスコープのところで簡単に触れましたが、大きく下記2点です。

・他の処理などから値の参照・変更を不可にできる。

・変数など使いまわしができる。

特に2つめのメリットはとても便利です。

今回は名前と出身地だけでしたが、その他にも血液型、性別、身長、体重など、いろいろな情報を保持する際に、安全に内包された変数に値を保持することができます。

そのため、大規模なアプリ開発や複数人での開発などでは必須の知識ですので、この機会に覚えて、ぜひ使ってみてくださいね。

大石ゆかり

内容が分かりやすくて良かったです!

田島悠介

ゆかりちゃんも分からないことがあったら質問してね!

大石ゆかり

分かりました。ありがとうございます!

 

JavaScriptを学習中の方へ

これで解説は終了です、お疲れさまでした。

  • つまずかず「効率的に」学びたい
  • 副業や転職後の「現場で使える」知識やスキルを身につけたい

プログラミングを学習していて、このように思ったことはありませんか?

テックアカデミーのフロントエンドコースでは、第一線で活躍する「プロのエンジニア」が教えているので、効率的に実践的なスキルを完全オンラインでしっかり習得できます。

合格率10%の選考を通過した、選ばれたエンジニアの手厚いサポートを受けながら、JavaScript・jQueryを使ったWebサービス開発を学べます。

まずは一度、無料体験で学習の悩みや今後のキャリアについて話してみて、「現役エンジニアから教わること」を実感してみてください。

時間がない方、深く知ってから体験してみたい方は、今スグ見られる説明動画から先に視聴することをおすすめします!