JavaScriptでのクロージャの実用的な用途は何ですか?


279

私はよしようとして JavaScriptの閉鎖のまわりで私の頭をラップするために、私の一番を。

内部関数を返すことで、直接の親で定義されている変数にアクセスできるようになります。

これはどこで役に立ちますか?たぶん、まだ頭が十分ではありません。私がオンラインで見例のほとんどは、実際のコードを提供しておらず、あいまいな例を提供しています。

誰かが私にクロージャの実際の使用を見せてくれますか?

これは、例えば?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

17
がんばろうとすることへの+1 :-)クロージャーは、最初は本当に気が遠くなるように思えるかもしれません。それらのコツをつかんだら、すぐにはるかに優れたコーダーになります。
Andy E

7
ちょうど私があなたがhelfpulを見つけるかもしれないJavaScriptの閉鎖についてのブログ投稿を書いたところです。
Skilldrick、2010年

@スキルドリック。リンクは死んでいます...また、この実用的な例は非常に役立つことがわかりました。youtube.com/watch?v=w1s9PgtEoJs
Abhi 2016

回答:


240

私はクロージャーを使って次のようなことをしました:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

ご覧のとおり、aは、を呼び出すメソッドpublicfunctiona.publicfunction())を持つオブジェクトになりました。これはprivatefunction、クロージャー内にのみ存在します。直接(つまり)を呼び出すことはできませんprivatefunctiona.privatefunction()publicfunction()

その最小限の例ですが、多分あなたはそれの用途を見ることができますか?これを使用してパブリック/プライベートメソッドを適用しました。


26
ああ、これがクロージャである場合、私はそれを知らずにクロージャを使用しました!私はしばしばそのような別の中に関数を置き、次にあなたの例のようにオブジェクトリテラルを返すことで必要なものを公開します。
アレックス2010

1
はい、ご覧のとおり、返されるオブジェクトは関数内の変数(および関数)を参照するため、関数のコンテキストを保持します。あなたはそれらを使用してきましたが、あなたはそれを知らなかっただけです。
フランシスコ・ソト

9
技術的には、ブラウザのJavaScriptで作成するすべての関数は、ウィンドウオブジェクトがバインドされているため、クロージャです。
Adam Gent

9
私はこれが古い質問であることを知っていますが、私にとってこれはまだ十分な答えを提供しません。関数を直接呼び出すだけではどうですか。なぜプライベート関数が必要なのですか?
qodeninja 14

5
この例には関数しかありませんが、外部からアクセスできない変数も含まれている可能性があるためです。説明:var obj =(function(){var value = 0; return {get:function(){return value;}、set:function(val){value = val;}}})(); obj.set(20); obj.get(); => 20など
Francisco Soto

210

ユーザーがWebページのボタンクリックした回数カウントするとします。
このためonclick、ボタンのイベントで関数をトリガーし て変数のカウントを更新します

<button onclick="updateClickCount()">click me</button>  

今、次のような多くのアプローチがあります:

1)グローバル変数と関数を使用してカウンターを増やすことができます:

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

しかし、落とし穴は、ページ上のスクリプトがを呼び出さなくてもカウンターを変更できることupdateClickCount()です。


2)今、あなたは関数内で変数を宣言することを考えているかもしれません:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

しかし、ねえ!updateClickCount()関数が呼び出されるたびに、カウンターは再び1に設定されます。


3)入れ子関数について考えていますか?

ネストされた関数は、それらの「上の」スコープにアクセスできます。
この例では、内部関数updateClickCount()は親関数のカウンター変数にアクセスできますcountWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

updateClickCount()外部から関数に到達でき、実行方法も見つける必要がある場合は、これでカウンタージレンマを解決できた可能性があります。counter = 0毎回ではなく一度だけ。


4)救助のための閉鎖!(自己呼び出し機能)

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

自己呼び出し機能は1回だけ実行されます。それはcounterをゼロ(0)、関数式を返します。

こちらです updateClickCountが関数になります。「素晴らしい」部分は、親スコープのカウンターにアクセスできることです。

これはJavaScriptクロージャと呼ばれます。関数が「プライベート」を持つことを可能にします」変数ます。

counter匿名関数の範囲によって保護されており、唯一の追加機能を使用して変更することができます!

Closureのより活発な例:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


リファレンス:https : //www.w3schools.com/js/js_function_closures.asp


49
これは私が言う作っ最初の答えである「ああ、それはだ、私はクロージャを使用する理由!」
Devil's Advocate 2017年

6
uは私の日を作りました:)
JerryGoyal 2017年

15
私はクロージャーのw3schoolsページを読んだ後、詳細についてここに来ました。これはw3schoolsページと同じです: w3schools.com/js/js_function_closures.asp
tyelford

1
@JerryGoyalは2つの別々のボタンで機能させることができますか?2つの変数(関数のコピー)を使用しないと、どのようにして主な利点/利便性の一部が取り除かれているかを理解できません。
タイラーコリアー

2
いい答えだ。ただし、クロージャーは自己呼び出し関数である必要ありません、そうである場合もあります。クロージャ自己呼び出しの場合(つまり、関数の後に()を追加することですぐに呼び出される)、これは、関数が返されて関数が呼び出された後で戻り値が計算されるのではなく、戻り値がすぐに計算されることを意味します。クロージャーは実際には別の関数内の任意の関数にすることができ、その主要な特徴は、変数やメソッドを含む親関数のスコープにアクセスできることです。
Chris Halcrow、2018

69

あなたが与える例は素晴らしいものです。クロージャーは、懸念を非常に明確に分離できる抽象化メカニズムです。あなたの例は、インストルメンテーション(呼び出しのカウント)をセマンティクス(エラー報告API)から分離する場合です。その他の用途は次のとおりです。

  1. パラメータ化された動作をアルゴリズムに渡す(古典的な高次プログラミング):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
  2. オブジェクト指向プログラミングのシミュレーション:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
  3. jQueryのイベント処理やAJAX APIなどのエキゾチックなフロー制御の実装。


3
int?)最後に確認したところ、JavaScriptはアヒル型の言語でした。多分あなたはJavaについて考えていましたか?
Hello71、2010年

1
@ Hello71:私はJavaScriptを考えていましたが、古い習慣はひどく死にます。良いキャッチ。
Marcelo Cantos、2010年

2
@MarceloCantosカウンターの実装でコンマを忘れたようです。あなたの投稿を編集して修正しました。大丈夫だといいね:)
ナタン・ストレッペル、2014年

2
@Streppel:良いキャッチ!私のコードを改善していただき、本当に嬉しいです。:-)
Marcelo Cantos 2014年

#1を理解しようとしています...どのようにプロキシミティソートを呼び出しますか?
Dave2081 2014

26

私はこの質問に答えるのが非常に遅いことを知っていますが、2018年にまだ答えを探している人を助けるかもしれません。

JavaScriptクロージャーを使用してスロットルデバウンスを実装できますて、アプリケーションに機能機能ます。

スロットル

スロットルは、時間の経過とともに関数を呼び出すことができる最大回数を制限します。「この関数は最大で100ミリ秒に1回実行する」のように。

コード:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

デバウンス

デバウンスは、関数が呼び出されずに一定の時間が経過するまで、再度呼び出されないように制限します。「この関数が呼び出されずに100ミリ秒が経過した場合にのみこの関数を実行する」のように。

コード:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

ご覧のとおり、クロージャーは2つの美しい機能の実装に役立ちました。これらの機能はすべてのWebアプリがスムーズなUIエクスペリエンス機能を提供するために必要です。

誰かのお役に立てば幸いです。


18

はい、これは便利なクロージャーの良い例です。warnUserを呼び出すとcalledCount、スコープ内に変数が作成され、warnForTamper変数に格納されている無名関数が返されます。calledCount変数を使用するクロージャーがまだ存在するため、関数の終了時に削除されないため、への呼び出しごとwarnForTamper()にスコープ変数が増加し、値が通知されます。

StackOverflowで最もよく見られる問題は、各ループで増加する変数の使用を「遅延」させたいところですが、変数がスコープされているため、変数への各参照はループが終了した後で行われ、結果として変数の終了状態:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

これにより、すべてのアラートに同じ値、iつまりループが終了したときに増加した値が表示されます。解決策は、変数の個別のスコープである新しいクロージャーを作成することです。これは、変数を受け取り、その状態を引数として保存する、即座に実行される無名関数を使用して行うことができます。

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

興味深い-1、これは「JavaScriptでのクロージャの実用的な使用」ではないと思いますか?
アンディE

1
私はそれを読むのにいくらか役立つと思ったので、反対票の前に+1を与えました。
アレックス2010

1
@alex:ありがとう、投票に気づきました。私はここSOで匿名の反対投票にほとんど慣れてきました。私が不正確または間違っていることを言ったかどうかを本当に知りたいので、それだけが私を困らせます、そして彼らはあなたがあなた自身の答えのより良い可視性を望んでいる他の回答者によってあなたがちょうど反対投票されたと思わせる傾向があります。幸い、私は復讐心のないタイプではありません;-)
Andy E

1
これは、JavaScriptの壊れたブロックスコープのより多くの回避策だと思います。var j = iを追加するだけです。最初のsetTimeoutの前に、そのjを使用するアラートを取得します。別の回避策は、「with」を次のように使用することです:for(var i = 0; i <someVar.length; i ++){with({i:i}){window.setTimeout(function(){alert( "Value ofこのタイマーが設定されたとき、私は "+ i +"でした ")}、100);}}
davidbuttar

1
@AndyEおかしいことは正しい言葉ではないかもしれません。このページの多くの回答のように、多くの人が自己呼び出し関数を使用してクロージャを説明していることに気づきました。ただし、setTimeoutのコールバック関数もクロージャーです。コールバックから他のローカル変数にアクセスできるため、「実用的」と見なされる場合があります。クロージャーについて学んでいたとき、これを理解することは私にとって便利でした-クロージャーはアーケードJavaScriptパターンだけでなく、どこにでもあるということです。
アントワーヌ2018

14

特にJavaScript(または任意のECMAScript)言語では、クロージャーは機能の実装を非表示にしながら、インターフェースを明らかにするのに役立ちます。

たとえば、日付ユーティリティメソッドのクラスを作成していて、ユーザーが曜日名をインデックスで検索できるようにしたいが、内部で使用する名前の配列を変更できないようにしたいとします。

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

ことに注意してくださいdays配列は、単にのプロパティとして保存することができdateUtilたオブジェクトが、それはスクリプトのユーザーに表示になり、彼らが望んでいた場合、彼らはさえも、あなたのソースコードを必要とせず、それを変更することができます。ただし、日付ルックアップ関数を返す無名関数で囲まれているため、ルックアップ関数からのみアクセスできるため、改ざん防止されています。


2
これは馬鹿げているように聞こえるかもしれませんが、JavaScriptファイル自体を開いて実装を確認することはできませんか?
itsmichaelwang

1
@Zapurdead:はい、もちろん実装を確認できますが、ソースコードを直接変更せずに(偶然または意図的に)実装を変更することはできません。Javaの保護されたメンバーと比較できると思います。
maerics 14

6

これを見てみるとreturn function ()...、コード全体を削除しても問題なく機能するように、「実用的」であることがわかりません。閉鎖は必須ではありません
ハサミムシ

@James_Parsons次に、例で行ったように、それらをイベントハンドラーに割り当てることができませんでした。
アレックス

5

クロージャのもう1つの一般的な用途thisは、メソッドで特定のオブジェクトにバインドして、他の場所(イベントハンドラなど)で呼び出せるようにすることです。

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

mousemoveイベントが発生するたびに、watcher.follow(evt)が呼び出されます。

クロージャは高次関数の重要な部分でもあり、類似しない部分をパラメータ化することにより、複数の類似した関数を単一の高次関数として書き換える非常に一般的なパターンを可能にします。抽象的な例として、

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

なる

fooer = function (x) {
    return function (...) {A x B}
}

ここで、AとBは構文単位ではなく、ソースコード文字列です(文字列リテラルではありません)。

具体的な例については、「関数によるJavaScriptの合理化」を参照してください。


5

ここで、何度か言いたい挨拶があります。クロージャーを作成したら、その関数を呼び出して挨拶を録音できます。クロージャーを作成しない場合、毎回名前を渡す必要があります。

クロージャーなし(https://jsfiddle.net/lukeschlangen/pw61qrow/3/):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

クロージャー付き(https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

1
確かではありませんが、クロージャーがなければ、次のように呼び出すことができます。grretBilly = greeting( "Billy"、 "Bob"); そしてgrretBilly();を呼び出します。それでも同じことをしますか?閉鎖を作成するかどうかは別の問題ですが、毎回名前を渡すことはここでは問題ではありません。
user2906608

4

オブジェクト指向の意味でのクラスのインスタンス化の概念に慣れている場合(つまり、そのクラスのオブジェクトを作成する場合)は、クロージャを理解することはほぼ可能です。

このように考えてください。2つのPersonオブジェクトをインスタンス化すると、クラスメンバー変数 "Name"がインスタンス間で共有されないことがわかります。各オブジェクトには独自の「コピー」があります。同様に、クロージャーを作成すると、自由変数(上記の例では「calledCount」)が関数の「インスタンス」にバインドされます。

あなたの概念的な飛躍は、warnUser関数によって返されるすべての関数/クロージャー(脇にあります:それは高階関数です)が同じ初期値(0)で 'calledCount'をバインドするという事実によってわずかに妨げられていると思いますクラスのコンストラクターに異なる値を渡すのと同じように、異なる初期化子を高階関数に渡す方が便利です。

したがって、「calledCount」が特定の値に達したときに、ユーザーのセッションを終了するとします。リクエストがローカルネットワークから送られてくるのか、大きなインターネットから送られてくるのかによって、異なる値が必要になる場合があります(そうです、これは不自然な例です)。これを達成するために、calledCountの異なる初期値をwarnUserに渡すことができます(つまり、-3、または0?)。

文献に関する問題の一部は、それらを説明するために使用される命名法です(「字句スコープ」、「自由変数」)。だまされてはいけません。クロージャは見た目よりも簡単です...一応;-)


3

ここでは、Eコマースサイトや他の多くのサイトで使用できる、クロージャコンセプトの簡単な例を1つ示します。私のjsfiddleリンクを例に追加しています。3つのアイテムの小さな製品リストと1つのカートカウンターが含まれています。

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>


2

クロージャーの使用:

クロージャーは、JavaScriptの最も強力な機能の1つです。JavaScriptは、関数のネストを許可し、内部関数に、外部関数内で定義されたすべての変数と関数(および外部関数がアクセスできる他のすべての変数と関数)へのフルアクセスを許可します。ただし、外部関数は、内部関数内で定義された変数および関数にアクセスできません。これは、内部関数の変数に一種のセキュリティを提供します。また、内部関数が外部関数のスコープにアクセスできるため、内部関数が外部関数の寿命を超えて存続できる場合、外部関数で定義された変数と関数は外部関数自体よりも長く存続します。

例:

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

上記のコードでは、外部関数の名前変数は内部関数からアクセスでき、内部関数を介して以外に内部変数にアクセスする方法はありません。内部関数の内部変数は、内部関数の安全なストアとして機能します。それらは、内部機能が動作するための「永続的」でありながら安全なデータを保持します。関数を変数に割り当てたり、名前を付けたりする必要はありません。詳細はこちら


2

Mozillaの関数ファクトリの例が好きです。

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);

11
これは、私の意見では、人々が閉鎖を理解するのに役立つタイプの例ではなく、彼らが何のために役立つのかを示していません。例として以外に、数値を追加する関数を返すクロージャーを何回書いたことがありますか?
モハマド2015年

2

JavaScriptモジュールパターンはクロージャーを使用します。その素敵なパターンにより、「パブリック」と「プライベート」の変数を同じようにすることができます。

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();


1

このスレッドは、クロージャーがどのように機能するかについての理解を深めるのに非常に役立ちました。それ以来私は自分自身でいくつかの実験を行い、このかなり単純なコードを思い付きました。これは、他の人々がクロージャを実際的な方法で使用する方法と、静的および/またはグローバル変数が上書きされたり、グローバル変数と混同されたりするリスクがないグローバル変数。これは、個々のボタンのローカルレベルとグローバルレベルの両方でボタンクリックを追跡し、すべてのボタンクリックをカウントして、1つの図に寄与します。これを行うためにグローバル変数を使用していないことに注意してください。これは演習の要点の1つです。グローバルに何かを提供するボタンに適用できるハンドラーがあります。

専門家にお願いします。私がここで悪い習慣を犯したかどうか教えてください!私はまだこのことを自分で学んでいます。

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>

0

リファレンス:クロージャの実用的な使い方

実際には、クロージャはエレガントなデザインを作成し、さまざまな計算、遅延呼び出し、コールバックのカスタマイズ、カプセル化されたスコープの作成などを可能にします。

引数として並べ替え条件関数を受け入れる配列の並べ替えメソッドの例:

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

関数の引数の条件によって新しい配列をマップする配列のマップメソッドとしての関数のマッピング:

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

多くの場合、ほとんど無制限の検索条件を定義する関数引数を使用して検索関数を実装すると便利です。

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

また、関数の適用は、たとえば、要素の配列に関数を適用するforEachメソッドとして適用できます。

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

関数は引数に適用されます(適用の場合は引数のリストに、呼び出しの場合は位置付けされた引数に)。

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

遅延呼び出し:

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

コールバック関数:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

補助オブジェクトを非表示にするためのカプセル化されたスコープの作成:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10

0

フロントエンドJavaScriptで作成するコードの多くはイベントベースです。いくつかの動作を定義し、それをユーザーがトリガーするイベント(クリックやキープレスなど)にアタッチします。通常、コードはコールバックとしてアタッチされます。イベントに応答して実行される単一の関数です。size12、size14、およびsize16は、本文テキストをそれぞれ12、14、16ピクセルにサイズ変更する関数になりました。次のようにボタン(この場合はリンク)に添付できます。

function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

フィドル


このコードは質問に答えることがありますが、問題を解決する方法および/または理由に関する追加のコンテキストを提供すると、回答の長期的な価値が向上します。
ドナルドダック

1
この例は、私には思えますが、標準関数を介してクロージャーなしで実装できます。私は閉鎖なしでは実装できないものの例を見つけようとしています
ザック・スミス

0

クロージャーは作成に役立つ方法です 、オンデマンドで増分されるシーケンス:

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

違いは次のように要約されます。

匿名関数定義済み関数

メソッドとしては使用できませんオブジェクトのメソッドとしては使用できます

定義されているスコープ内にのみ存在する定義されているオブジェクト内に存在する

定義されたスコープ内でのみ呼び出すことができますコードの任意の時点で呼び出すことができます

新しい値を再割り当てまたは削除できます削除または変更できません

参考文献


0

与えられたサンプルでは、​​囲まれた変数 'c​​ounter'の値は保護されており、与えられた関数(インクリメント、デクリメント)を使用しないと変更できません。クロージャーに入っているので

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2


0

JavaScriptでのクロージャの実用的な使い方を説明する---

別の関数内に関数を作成すると、クロージャーが作成されます。クロージャーは、外部関数のデータを読み取り、操作できるため、強力です。関数が呼び出されるたびに、その呼び出しに対して新しいスコープが作成されます。関数内で宣言されたローカル変数はそのスコープに属し、それらはその関数からのみアクセスできます。関数の実行が完了すると、通常、スコープは破棄されます。

このような関数の簡単な例は次のとおりです。

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

上記の例では、関数buildName()がローカル変数のgreetingを宣言し、それを返します。すべての関数呼び出しは、新しいローカル変数で新しいスコープを作成します。関数の実行が完了すると、そのスコープを再度参照する方法がないため、ガベージコレクションされます。

しかし、そのスコープへのリンクがある場合はどうでしょうか?

次の関数を見てみましょう:

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

この例の関数sayName()はクロージャーです。sayName()関数には独自のローカルスコープ(welcome変数あり)があり、外部(囲んでいる)関数のスコープにもアクセスできます。この場合、buildName()からのあいさつ変数です。

buildNameの実行が完了した後、この場合、スコープは破棄されません。sayMyName()関数は引き続きアクセスできるため、ガベージコレクションは行われません。ただし、クロージャーを除いて、外部スコープからデータにアクセスする方法は他にありません。クロージャは、グローバルコンテキストと外部スコープの間のゲートウェイとして機能します。


0

私はクルーザを学びたいと思っています、そして私が作成した例は実用的なユースケースだと思います。スニペットを実行して、コンソールで結果を確認できます。別々のデータを持つ2人の別々のユーザーがいます。それぞれが実際の状態を確認して更新できます。

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.