TIL 19. Java Script - Closure
23758 ワード
JavaScript固有の概念ではないが,関数式プログラミング言語(Functional Programming Language)で用いられる重要な特性「closure」を学習した.
「シャーシ」(Close)の概念
ソース
Engineering Blog by Dale Seo
Webプログラミングチュートリアル/Poiemaweb
「シャーシ」(Close)の概念
Closerとは、Aという関数が内部の変数だけでなく、その内部に含まれる親Scope(外部関数B)にもアクセスできる変数です.ここでの外部関数には、モジュールを囲む外部関数とグローバルスキャンも含まれます.function B (result) {
const rate = 10;
const A = (value) = value * rate; // A함수 외부에 있는 B함수에서 rate 변수 접근
return result.map(A)
}
B([1, 2, 3]); // [10, 20, 30] 출력
const rate = 10;
function A(value) {
return value * rate // 전역 스코프에서 rate 변수 접근
}
A(1); // 10 출력
前述したように、モジュールによってアクセス可能な外部関数の変数(rate)を自由変数と呼び、ここでは、モジュール(closure)の名前が自由変数中の関数がインターリーブされ、クローズド(closed)と呼ばれることがわかる.
エンクロージャフィーチャー
1.外部関数が返されても、1つ以上の内部関数が必要でない限り、外部関数内の変数は変更されません.
モジュールの特徴は,特に内部関数が自分を含む外部関数よりも長い時間保持されている場合,外部関数が外部関数を呼び出しても外部関数の領域変数にアクセスできることである.
たとえば、外部関数が内部関数を返すと、直ちに関数が実行されるとします.宣言後すぐに関数が実行されると、外部関数のライフサイクルが終了し、呼び出しスタック(コンテキストスタックの実行)から削除され、関数の変数も無効になります.ただし、外部関数の内部関数をエンクロージャを介して別の場所から呼び出すと、外部関数の変数が機能します.つまり、エンクロージャは、宣言時に返された内部関数のディレクトリ環境を記憶し、外部から環境にアクセスできます.
下図に示すように、関数Bはcallスタックから除去されるが、返される内部関数Aにはレート変数が必要であるため、変わらない.function B() {
const rate = 10;
const A = function () {document.write(rate); };
return A;
} // 함수 B는 함수 A를 반환하면서 life-cycle을 마감함
const inside = B();
inside(); // 10 출력
2.ある関数の内部で定義された直後に呼び出される使い捨て関数のパラメータを省略できます.
以下の場合、sayHi()
関数の内部にはinformationNoitce()
関数とlogInCount()
関数があるので、内部関数を記述するパラメータを必要とせずに自由変数を導入することができ、関数呼び出し時にもパラメータを渡す必要がない.しかし、sayHi()
関数を除外すると、informationNoitce()
関数とlogInCount()
関数にはそれぞれパラメータが必要になり、sayHi()
関数でこの2つの関数を呼び出す場合にもパラメータが必要になります.function sayHi(id, level, logIn) {
const informationNoitce = function() {
alert(`${id}님은 level ${level} 입니다.`)
}
const logInCount = function() {
console.log(`${logIn}`)
}
informationNoitce() // 인자 없음
logInCount() // 인자 없음
}
3.エンクロージャが重複している可能性があるため、エンクロージャが多ければ多いほど、コードのメンテナンスが困難になります.
下図に示すように、rate
変数を除外すると、showData()
関数は閉パケットを生成し、calcRate()
関数はオーバーラップ閉パケットを生成します.これにより、関数が非常に長い可能性が高くなり、変数が外部でlet
のキーワードとして使用されるため、外部で自由に変更できるという問題が発生する可能性がある.したがって,モジュールのオーバーラップをできるだけ回避したり,変数を関数のパラメータとして加えたりすることで,変数が外部で修正される可能性のある問題を回避できる.let rate = 10;
function showData(value) {
const calcRate = (value) => value * rate
return value.map(calcRate)
}
エンクロージャの使用
:::最新のまま
以下は切り替えボタンをクリックしたときです.box要素を非表示または非表示にする機能を実現しました.toggle
関数内で返される関数は、自由変数isShow
に近い閉パケットである.このボタンをクリックして、イベントハンドラに割り当てられたイベントハンドラキャビネットを呼び出します.このときbox要素表示状態を表す変数isShow
の値が変更されます.変数isShow
は、エンクロージャによって参照されるため有効であり、自身の変更の最新の状態を維持し続けます.したがって、ボタンをクリックすると、true
とfalse
が交互に表示されます.<html>
<body>
<button class="toggle">toggle</button>
<div class="box" style="width: 100px; height: 100px; background: red;"></div>
<script>
const box = document.querySelector('.box');
const toggleBtn = document.querySelector('.toggle');
const toggle = (function () {
// 변수
let isShow = false;
// 클로저
return function () {
box.style.display = isShow ? 'block' : 'none';
isShow = !isShow;
};
})();
toggleBtn.addEventListner('click', toggle);
</script>
</body>
</html>
::グローバル変数の無効化
グローバル変数はいつでもアクセスでき、いつでも変更できるため、エラーが発生することがあります.モジュールを使用してグローバル変数を関数として記述すると、これらのエラーを回避できます.
下図に示すように、increaseBtn
ボタンのクリック時に1とカウントされる関数を作成すると、increase
関数の外部でcount
変数を宣言せず、内部で宣言する.ただし、count
変数をエンクロージャ内部の領域変数として宣言する場合は、ボタンをクリックするたびにcount
値が0に初期化されることに注意してください.<html>
<body>
<button id="inclease">+</button>
<p id="count">0</p>
<script>
const increaseBtn = document.getElementById('inclease');
const count = document.getElementById('count');
const increase = (function () {
// 변수
let counter = 0;
// 클로저
return function () {
return ++counter;
};
}());
increaseBtn.onclick = function () {
count.innerHTML = increase();
};
</script>
</body>
</html>
::情報を非表示
サブ関数Counter
が生成され、increase
、decrease
の方法を有する例が生成される.この場合、両方のメソッドは、作成時のディレクトリ環境を共有し、モジュールとして変数にアクセスします.このとき、let counter = 0;
はプロ選手ではなく変数です.この変数は、コンストラクション関数カウンタから外部にアクセスできません.したがって、変数に外部からアクセスして値を変更することはできません.function Counter() {
// 카운트를 유지하기 위한 자유 변수
let counter = 0;
// 클로저
this.increase = function () {
return ++counter;
};
// 클로저
this.decrease = function () {
return --counter;
};
}
const counter = new Counter();
console.log(counter.increase()); // 0에서 +1이 되어 '1'출력
console.log(counter.decrease()); // 최신 상태인 1에서 -1 되어 '0'출력
function B (result) {
const rate = 10;
const A = (value) = value * rate; // A함수 외부에 있는 B함수에서 rate 변수 접근
return result.map(A)
}
B([1, 2, 3]); // [10, 20, 30] 출력
const rate = 10;
function A(value) {
return value * rate // 전역 스코프에서 rate 변수 접근
}
A(1); // 10 출력
1.外部関数が返されても、1つ以上の内部関数が必要でない限り、外部関数内の変数は変更されません.
モジュールの特徴は,特に内部関数が自分を含む外部関数よりも長い時間保持されている場合,外部関数が外部関数を呼び出しても外部関数の領域変数にアクセスできることである.
たとえば、外部関数が内部関数を返すと、直ちに関数が実行されるとします.宣言後すぐに関数が実行されると、外部関数のライフサイクルが終了し、呼び出しスタック(コンテキストスタックの実行)から削除され、関数の変数も無効になります.ただし、外部関数の内部関数をエンクロージャを介して別の場所から呼び出すと、外部関数の変数が機能します.つまり、エンクロージャは、宣言時に返された内部関数のディレクトリ環境を記憶し、外部から環境にアクセスできます.
下図に示すように、関数Bはcallスタックから除去されるが、返される内部関数Aにはレート変数が必要であるため、変わらない.
function B() {
const rate = 10;
const A = function () {document.write(rate); };
return A;
} // 함수 B는 함수 A를 반환하면서 life-cycle을 마감함
const inside = B();
inside(); // 10 출력
2.ある関数の内部で定義された直後に呼び出される使い捨て関数のパラメータを省略できます.以下の場合、
sayHi()
関数の内部にはinformationNoitce()
関数とlogInCount()
関数があるので、内部関数を記述するパラメータを必要とせずに自由変数を導入することができ、関数呼び出し時にもパラメータを渡す必要がない.しかし、sayHi()
関数を除外すると、informationNoitce()
関数とlogInCount()
関数にはそれぞれパラメータが必要になり、sayHi()
関数でこの2つの関数を呼び出す場合にもパラメータが必要になります.function sayHi(id, level, logIn) {
const informationNoitce = function() {
alert(`${id}님은 level ${level} 입니다.`)
}
const logInCount = function() {
console.log(`${logIn}`)
}
informationNoitce() // 인자 없음
logInCount() // 인자 없음
}
3.エンクロージャが重複している可能性があるため、エンクロージャが多ければ多いほど、コードのメンテナンスが困難になります.下図に示すように、
rate
変数を除外すると、showData()
関数は閉パケットを生成し、calcRate()
関数はオーバーラップ閉パケットを生成します.これにより、関数が非常に長い可能性が高くなり、変数が外部でlet
のキーワードとして使用されるため、外部で自由に変更できるという問題が発生する可能性がある.したがって,モジュールのオーバーラップをできるだけ回避したり,変数を関数のパラメータとして加えたりすることで,変数が外部で修正される可能性のある問題を回避できる.let rate = 10;
function showData(value) {
const calcRate = (value) => value * rate
return value.map(calcRate)
}
エンクロージャの使用
:::最新のまま
以下は切り替えボタンをクリックしたときです.box要素を非表示または非表示にする機能を実現しました.toggle
関数内で返される関数は、自由変数isShow
に近い閉パケットである.このボタンをクリックして、イベントハンドラに割り当てられたイベントハンドラキャビネットを呼び出します.このときbox要素表示状態を表す変数isShow
の値が変更されます.変数isShow
は、エンクロージャによって参照されるため有効であり、自身の変更の最新の状態を維持し続けます.したがって、ボタンをクリックすると、true
とfalse
が交互に表示されます.<html>
<body>
<button class="toggle">toggle</button>
<div class="box" style="width: 100px; height: 100px; background: red;"></div>
<script>
const box = document.querySelector('.box');
const toggleBtn = document.querySelector('.toggle');
const toggle = (function () {
// 변수
let isShow = false;
// 클로저
return function () {
box.style.display = isShow ? 'block' : 'none';
isShow = !isShow;
};
})();
toggleBtn.addEventListner('click', toggle);
</script>
</body>
</html>
::グローバル変数の無効化
グローバル変数はいつでもアクセスでき、いつでも変更できるため、エラーが発生することがあります.モジュールを使用してグローバル変数を関数として記述すると、これらのエラーを回避できます.
下図に示すように、increaseBtn
ボタンのクリック時に1とカウントされる関数を作成すると、increase
関数の外部でcount
変数を宣言せず、内部で宣言する.ただし、count
変数をエンクロージャ内部の領域変数として宣言する場合は、ボタンをクリックするたびにcount
値が0に初期化されることに注意してください.<html>
<body>
<button id="inclease">+</button>
<p id="count">0</p>
<script>
const increaseBtn = document.getElementById('inclease');
const count = document.getElementById('count');
const increase = (function () {
// 변수
let counter = 0;
// 클로저
return function () {
return ++counter;
};
}());
increaseBtn.onclick = function () {
count.innerHTML = increase();
};
</script>
</body>
</html>
::情報を非表示
サブ関数Counter
が生成され、increase
、decrease
の方法を有する例が生成される.この場合、両方のメソッドは、作成時のディレクトリ環境を共有し、モジュールとして変数にアクセスします.このとき、let counter = 0;
はプロ選手ではなく変数です.この変数は、コンストラクション関数カウンタから外部にアクセスできません.したがって、変数に外部からアクセスして値を変更することはできません.function Counter() {
// 카운트를 유지하기 위한 자유 변수
let counter = 0;
// 클로저
this.increase = function () {
return ++counter;
};
// 클로저
this.decrease = function () {
return --counter;
};
}
const counter = new Counter();
console.log(counter.increase()); // 0에서 +1이 되어 '1'출력
console.log(counter.decrease()); // 최신 상태인 1에서 -1 되어 '0'출력
<html>
<body>
<button class="toggle">toggle</button>
<div class="box" style="width: 100px; height: 100px; background: red;"></div>
<script>
const box = document.querySelector('.box');
const toggleBtn = document.querySelector('.toggle');
const toggle = (function () {
// 변수
let isShow = false;
// 클로저
return function () {
box.style.display = isShow ? 'block' : 'none';
isShow = !isShow;
};
})();
toggleBtn.addEventListner('click', toggle);
</script>
</body>
</html>
<html>
<body>
<button id="inclease">+</button>
<p id="count">0</p>
<script>
const increaseBtn = document.getElementById('inclease');
const count = document.getElementById('count');
const increase = (function () {
// 변수
let counter = 0;
// 클로저
return function () {
return ++counter;
};
}());
increaseBtn.onclick = function () {
count.innerHTML = increase();
};
</script>
</body>
</html>
function Counter() {
// 카운트를 유지하기 위한 자유 변수
let counter = 0;
// 클로저
this.increase = function () {
return ++counter;
};
// 클로저
this.decrease = function () {
return --counter;
};
}
const counter = new Counter();
console.log(counter.increase()); // 0에서 +1이 되어 '1'출력
console.log(counter.decrease()); // 최신 상태인 1에서 -1 되어 '0'출력
Engineering Blog by Dale Seo
Webプログラミングチュートリアル/Poiemaweb
Reference
この問題について(TIL 19. Java Script - Closure), 我々は、より多くの情報をここで見つけました https://velog.io/@smy/TIL-19.-Java-Script-miniproject-this-closureテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol