Help us understand the problem. What is going on with this article?

JavaScriptのクロージャの初歩の初歩

More than 3 years have passed since last update.

はじめに

クロージャって何?

クロージャとは、関数内に現れる変数の名前解決が、ローカルスコープだけでなく、関数が定義された場所のスコープも含めて行われる関数のことです。
Perfect PHP

関数の中に関数を書く

クロージャを理解するために、関数の中の関数を使うコードを書いてみます。まずは関数リテラルをそのままreturnする方法。

inner_func1.js
function f() {
    return function() {
        console.log("Hello!");
        return;
    };
}
var fn = f();
fn(); //Hello!
fn(); //Hello!

あるいは、こんな風にも書けます。関数リテラル式を変数に入れてからreturnする方法。

inner_func2.js
function f2() {
    var inner = function () {
        console.log("Yo!");
        return;
    };
    return inner;
}

var fn2 = f2();
fn2(); //Yo!
fn2(); //Yo!

こんな風に書き直すこともできます。関数を一旦定義してから、関数名をreturnする方法。

inner_func3.js
function f3() {
    function inner() {
        console.log("Hi!");
        return;
    };
    return inner;
}

var fn3 = f3();
fn3(); //Hi!
fn3(); //Hi!

クロージャの例

Perfect JavaScriptのクロージャのサンプルコードです。

sample.js
function f() {
    var cnt = 0;
    var inner = function() {
        return ++cnt;
    };
    return inner;
}

var fn = f();
console.log(fn()); //1
console.log(fn()); //2
console.log(fn()); //3

cntは関数fの外からは参照できないですが、クロージャ(inner)からは参照することができます。ここまで来ると、冒頭の「クロージャって何?」の意味が分かる気がします。
なお、ここではinner_func2.jsの形式を使って関数を定義しましたが、他の形式でも同様の動作をさせることができます。

二つの関数を入れる

関数fの中に、2つのクロージャを入れることもできます。

double_closure.js
function f() {
    var cnt = 0;
    var add = function() {
        return ++cnt;
    };
    var sub = function() {
        return --cnt;
    };
    return {add:add, sub:sub};
}

var fn = f();
console.log(fn.add());
console.log(fn.add());
console.log(fn.sub());
console.log(fn.sub());

2つのクロージャから、同じ変数cntを参照できます。

Classっぽく書く

thisを使って、クラスっぽく書くこともできます。

my_class.js
function MyClass() {
    this.cnt = 0;
    this.add = function() {
        return ++this.cnt;
    };
    this.sub = function() {
        return --this.cnt;
    };
    this.show = function() {
        console.log(this.cnt);
    };
    return this;
}

var fn = MyClass();
console.log(fn.add()); //1
console.log(fn.add()); //2
console.log(fn.sub()); //1
console.log(fn.sub()); //0

// 直接操作できちゃう
fn.cnt = 10;
fn.show(); //10
fn.add();
fn.show(); //11

MyClass内の関数から参照する変数を、this.cntとすれば外部から直接参照可能になります。次のようにローカル変数(var cntで宣言)とすれば外部からは直接参照不可になります。

my_class2.js
function MyClass() {
    var cnt = 0;
    this.add = function() {
        return ++cnt;
    };
    this.sub = function() {
        return --cnt;
    };
    this.show = function() {
        console.log(cnt);
    };
    return this;
}

var fn = MyClass();
console.log(fn.add()); //1
console.log(fn.add()); //2
console.log(fn.sub()); //1
console.log(fn.sub()); //0

// 直接操作できない
//fn.cnt = 10;
fn.show(); //0
fn.add();
fn.show(); //1

初期値を設定する

fの引数を利用するなどして、クロージャから参照する変数の初期値を与えることもできます。

set_init.js
function f(cnt_init) {
    var cnt = cnt_init;
    var add = function() {
        return ++cnt;
    };
    var sub = function() {
        return --cnt;
    };
    return {add:add, sub:sub};
}

var fn = f(10);
console.log(fn.add()); //11
console.log(fn.add()); //12
console.log(fn.sub()); //11
console.log(fn.sub()); //10
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした