나는 카레에 관한 질문을하고 폐쇄가 언급되었다. 폐쇄 란 무엇입니까? 카레와는 어떤 관계가 있습니까?
나는 카레에 관한 질문을하고 폐쇄가 언급되었다. 폐쇄 란 무엇입니까? 카레와는 어떤 관계가 있습니까?
답변:
지역 변수를 선언하면 해당 변수에 범위가 있습니다. 일반적으로 지역 변수는 선언 한 블록이나 함수 내에 만 존재합니다.
function() {
var a = 1;
console.log(a); // works
}
console.log(a); // fails
로컬 변수에 액세스하려고하면 대부분의 언어가 현재 범위에서 변수를 찾고 루트 범위에 도달 할 때까지 부모 범위를 통해 찾습니다.
var a = 1;
function() {
console.log(a); // works
}
console.log(a); // works
블록이나 함수가 완료되면 로컬 변수가 더 이상 필요하지 않으며 일반적으로 메모리가 부족합니다.
이것이 우리가 정상적으로 작동하는 방식입니다.
클로저는 코드 실행이 해당 블록 밖으로 이동 한 후에도 로컬 변수를 유지하는 영구 범위입니다. 클로저를 지원하는 언어 (예 : JavaScript, Swift 및 Ruby)를 사용하면 해당 변수가 선언 된 블록의 실행이 완료된 후에도 참조를 유지하는 경우에도 범위 (상위 범위 포함)에 대한 참조를 유지할 수 있습니다. 그 블록이나 기능에 어딘가에.
스코프 객체와 모든 로컬 변수는 함수에 연결되며 해당 함수가 지속되는 한 지속됩니다.
이것은 우리에게 기능 이식성을 제공합니다. 함수가 처음 정의되었을 때 범위 내에 있던 변수는 나중에 완전히 다른 컨텍스트에서 함수를 호출하더라도 나중에 함수를 호출 할 때 범위 내에있는 것으로 예상 할 수 있습니다.
요점을 보여주는 JavaScript의 간단한 예제는 다음과 같습니다.
outer = function() {
var a = 1;
var inner = function() {
console.log(a);
}
return inner; // this returns a function
}
var fnc = outer(); // execute outer to get inner
fnc();
여기에 함수 내에서 함수를 정의했습니다. 내부 함수는를 포함하여 모든 외부 함수의 로컬 변수에 액세스 할 수 a
있습니다. 변수 a
는 내부 함수의 범위에 있습니다.
일반적으로 함수가 종료되면 모든 로컬 변수가 사라집니다. 그러나 내부 함수를 반환하고 변수 가 종료 된 fnc
후에도 지속되도록 변수에 할당하면 정의 되었을 때 범위 내에 있던 모든 변수 도 유지됩니다 . 변수 가 닫혔습니다 . 변수 가 닫힙니다.outer
inner
a
변수 a
는 전적으로 개인용 fnc
입니다. 이는 JavaScript와 같은 기능적 프로그래밍 언어로 개인 변수를 작성하는 방법입니다.
당신이 짐작할 수 있듯이, 호출 fnc()
하면 a
"1" 의 값을 인쇄합니다 .
클로저가없는 언어에서는 변수 a
가 가비지 수집되어 함수 outer
가 종료 될 때 버려졌습니다 . a
더 이상 존재하지 않으므로 fnc를 호출하면 오류가 발생했습니다 .
JavaScript a
에서 변수 범위는 함수가 처음 선언 될 때 작성되고 함수가 계속 존재하는 한 지속되므로 변수 가 지속됩니다.
a
의 범위에 속합니다 outer
. 의 범위에는의 범위에 inner
대한 부모 포인터가 outer
있습니다. fnc
를 가리키는 변수입니다 inner
. a
지속되는 한 fnc
지속됩니다. a
폐쇄 내에 있습니다.
JavaScript를 사용하여 예를 들어 보겠습니다.
function makeCounter () {
var count = 0;
return function () {
count += 1;
return count;
}
}
var x = makeCounter();
x(); returns 1
x(); returns 2
...etc...
이 함수 인 makeCounter는 x를 호출 한 함수가 호출 될 때마다 하나씩 카운트되는 함수를 반환합니다. x에 매개 변수를 제공하지 않기 때문에 어떻게 든 카운트를 기억해야합니다. 어휘 범위 지정을 기반으로 찾을 위치를 알고 있습니다. 값을 찾기 위해 정의 된 지점을 찾아야합니다. 이 "숨겨진"값을 클로저라고합니다.
여기 내 커리 예가 있습니다.
function add (a) {
return function (b) {
return a + b;
}
}
var add3 = add(3);
add3(4); returns 7
당신이 볼 수있는 것은 매개 변수 a (3)로 add를 호출하면 add3으로 정의 된 반환 된 함수의 클로저에 해당 값이 포함된다는 것입니다. 이런 식으로 add3을 호출하면 덧셈을 수행 할 값을 찾을 위치를 알 수 있습니다.
우선, 대부분의 사람들이 말한 것과는 반대로, 폐쇄는 기능 이 아닙니다 ! 그래서 입니다 그것은?
그것은 인 세트 (그 알려진 함수의 "주위 환경"의 정의 심볼 환경 그것 (즉, 각 심볼을 정의하는 값을 갖는다되는 표현식이 때문에이 평가 될 수있다) 밀폐 식 확인).
예를 들어, JavaScript 함수가있는 경우 :
function closed(x) {
return x + 3;
}
그것은이다 폐쇄 표현 그것에서 발생하는 모든 기호는 그 안에 정의되어 있기 때문에 당신이 그것을 평가할 수 있도록, (그 의미가 명확하다). 즉, 자체 포함되어 있습니다.
그러나 다음과 같은 기능이 있다면 :
function open(x) {
return x*y + 3;
}
그것은 인 오픈 식 그것에서 정의되지 않은 그것의 심볼들이 존재하기 때문이다. 즉 y
. 이 함수를 볼 때, y
그 의미와 의미를 알 수 없으며, 그 가치를 모릅니다. 따라서이 표현을 평가할 수 없습니다. 즉, y
의미가 무엇인지 알 때 까지이 함수를 호출 할 수 없습니다 . 이것을 자유 변수y
라고 합니다 .
이것은 y
정의를 요구하지만이 정의는 함수의 일부가 아닙니다. "주변 컨텍스트"( 환경 이라고도 함)의 다른 곳에 정의되어 있습니다 . 적어도 그것이 우리가 바라는 것입니다 : P
예를 들어, 다음과 같이 전역 적으로 정의 할 수 있습니다.
var y = 7;
function open(x) {
return x*y + 3;
}
또는 그것을 감싸는 함수로 정의 될 수 있습니다 :
var global = 2;
function wrapper(y) {
var w = "unused";
return function(x) {
return x*y + 3;
}
}
표현에서 자유 변수에 의미를 부여하는 환경의 일부는 클로저 입니다. 이 방법 은 모든 자유 변수에 대해 누락 된 정의를 제공 하여 열린 표현식을 닫힌 표현식으로 변환 하므로 평가할 수 있습니다.
위의 예에서 내부 함수 (필요하지 않기 때문에 이름을 지정하지 않은)는 변수 가 자유 로워 개방형 표현식입니다. 정의는 함수 외부에 있습니다. . 해당 익명 함수 의 환경 은 변수 세트입니다.y
{
global: 2,
w: "unused",
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
이제 폐쇄 는 모든 자유 변수에 대한 정의를 제공하여 내부 기능 을 닫는 이 환경의 일부입니다 . 우리의 경우 내부 함수의 유일한 자유 변수는 이므로 해당 함수의 폐쇄는 환경의 하위 집합입니다.y
{
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
환경에 정의 된 다른 두 기호는 하지 의 부분 폐쇄 가 실행을 필요로하지 않기 때문에, 그 함수의. 그들은 그것을 닫을 필요가 없습니다 .
그 뒤에있는 이론에 대한 자세한 내용은 https://stackoverflow.com/a/36878651/434562
위의 예제에서 래퍼 함수는 내부 함수를 값으로 반환합니다. 이 함수를 호출하는 순간은 함수가 정의 된 (또는 생성 된) 순간부터 원격이 될 수 있습니다. 특히, 랩핑 기능이 더 이상 실행되지 않고 호출 스택에있는 해당 매개 변수가 더 이상 존재하지 않습니다 .P 내부 함수 y
가 호출 될 때 내부 함수가 있어야 하기 때문에 문제 가됩니다! 다시 말해, 랩퍼 함수 보다 오래 지속 되고 필요할 때 거기에 있도록 변수의 클로저에서 변수 가 필요합니다. 따라서 내부 함수는 이러한 변수 의 스냅 샷 을 만들어서 닫히고 나중에 사용할 수 있도록 안전한 곳에 보관해야합니다. (콜 스택 외부의 어딘가에 있습니다.)
그렇기 때문에 사람들은 종종 폐쇄 라는 용어 를 그들이 사용하는 외부 변수의 스냅 샷을 만들 수있는 특수한 유형의 함수 또는 나중에 이러한 변수를 저장하는 데 사용되는 데이터 구조로 혼동하는 이유 입니다. 그러나 나는 당신이 그들이 이니까 이해 희망 하지 폐쇄 자체 - 그들은 단지 방법입니다 구현 필요할 때 함수의 폐쇄에서 변수가 될 수있는 프로그래밍 언어 또는 언어 메커니즘에 클로저를. 클로저에 대한 많은 오해가 있습니다.이 주제는 (불필요하게)이 주제를 실제로보다 혼란스럽고 복잡하게 만듭니다.
클로저에 대한 이해를 돕기 위해 프로 시저 언어로 어떻게 구현되는지 조사하는 것이 유용 할 수 있습니다. 이 설명은 Scheme에서 클로저를 간단하게 구현 한 후 따릅니다.
시작하려면 네임 스페이스 개념을 소개해야합니다. Scheme 인터프리터에 명령을 입력하면 표현식의 다양한 기호를 평가하고 해당 값을 가져와야합니다. 예:
(define x 3)
(define y 4)
(+ x y) returns 7
정의 표현식은 x의 자리에 값 3을, y의 자리에 값 4를 저장합니다. 그런 다음 (+ xy)를 호출하면 인터프리터는 네임 스페이스에서 값을 찾고 작업을 수행하고 7을 반환 할 수 있습니다.
그러나 구성표에는 기호 값을 임시로 재정의 할 수있는 표현식이 있습니다. 예를 들면 다음과 같습니다.
(define x 3)
(define y 4)
(let ((x 5))
(+ x y)) returns 9
x returns 3
let 키워드의 기능은 x가 값 5 인 새로운 네임 스페이스를 도입하는 것입니다. y가 4라는 것을 여전히 볼 수 있으며, 합계는 9로 반환됩니다. 이런 의미에서 x는 일시적으로 로컬 값으로 가려져 있습니다.
절차 언어와 객체 지향 언어는 비슷한 개념을 가지고 있습니다. 전역 변수와 이름이 같은 함수에서 변수를 선언 할 때마다 동일한 효과를 얻습니다.
우리는 이것을 어떻게 구현할 것입니까? 간단한 방법은 연결된 목록을 사용하는 것입니다. 머리에는 새로운 값이 포함되고 꼬리에는 오래된 네임 스페이스가 포함됩니다. 기호를 찾아야 할 때 머리부터 시작하여 꼬리를 따라 내려갑니다.
이제는 일류 함수 구현으로 건너 뛰겠습니다. 대체로 함수는 함수가 반환 값에서 절정이라고 할 때 실행할 명령 집합입니다. 함수를 읽을 때 이러한 명령어를 씬 뒤에 저장하고 함수가 호출 될 때 실행할 수 있습니다.
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns ?
x를 3으로 정의하고 더하기 x를 매개 변수 y로 정의하고 x 값을 정의합니다. 마지막으로 우리는 x가 새로운 x에 의해 가려진 환경에서 더하기 -x를 호출합니다.이 값은 5입니다. x가 5 인 경우 반환되는 결과는 9입니다. 이것이 동적 범위 지정입니다.
그러나 Scheme, Common Lisp 및 기타 여러 언어에는 어휘 범위 지정 기능이 있습니다. 연산 저장 (+ xy) 외에도 해당 특정 지점에 네임 스페이스도 저장합니다. 이렇게하면 값을 찾을 때이 문맥에서 x가 실제로 3이라는 것을 알 수 있습니다. 이것은 폐쇄입니다.
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns 7
요약하면, 링크 된리스트를 사용하여 함수를 정의 할 때 네임 스페이스의 상태를 저장할 수 있으며, 둘러싸는 범위에서 변수에 액세스 할 수있을뿐만 아니라 나머지 부분에 영향을주지 않고 변수를 로컬로 마스킹 할 수 있습니다. 프로그램.
Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
Closures가 엉덩이를 걷어차는 이유에 대한 실제 예는 다음과 같습니다. 이것은 Javascript 코드와 완전히 다릅니다. 설명하겠습니다.
Function.prototype.delay = function(ms /*[, arg...]*/) {
var fn = this,
args = Array.prototype.slice.call(arguments, 1);
return window.setTimeout(function() {
return fn.apply(fn, args);
}, ms);
};
사용 방법은 다음과 같습니다.
var startPlayback = function(track) {
Player.play(track);
};
startPlayback(someTrack);
이제이 코드 스 니펫이 실행 된 후 5 초 후에 재생이 지연되기를 원한다고 상상해보십시오. 글쎄 그것은 쉽고 delay
닫힙니다.
startPlayback.delay(5000, someTrack);
// Keep going, do other things
당신이 호출 할 때 delay
와 5000
MS, 최초의 조각 실행 및 저장 그것의 폐쇄에 인수 전달. 그런 다음 5 초 후에 setTimeout
콜백이 발생할 때 클로저는 여전히 해당 변수를 유지하므로 원래 매개 변수를 사용하여 원래 함수를 호출 할 수 있습니다.
이것은 일종의 카레 또는 기능 장식입니다.
클로저가 없으면 함수 외부에서 변수의 상태를 어떻게 유지해야하므로 함수 외부에 코드가 논리적으로 속합니다. 클로저를 사용하면 코드의 품질과 가독성이 크게 향상 될 수 있습니다.
var pure = function pure(x){
return x
// only own environment is used
}
var foo = "bar"
var closure = function closure(){
return foo
// foo is a free variable from the outer environment
}
클로저는 함수와 그 범위가 변수에 할당되거나 변수로 사용됩니다. 따라서 이름 클로저 : 범위와 함수는 다른 엔터티처럼 닫히고 사용됩니다.
Wikipedia에 따르면 폐쇄 는 다음과 같습니다.
일급 함수를 사용하는 언어로 어휘 범위가 지정된 이름 바인딩을 구현하는 기술.
그게 무슨 뜻이야? 몇 가지 정의를 살펴 보겠습니다.
이 예제를 사용하여 클로저 및 기타 관련 정의를 설명합니다.
function startAt(x) {
return function (y) {
return x + y;
}
}
var closure1 = startAt(1);
var closure2 = startAt(5);
console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)
기본적으로 이는 다른 엔티티와 마찬가지로 함수를 사용할 수 있음을 의미 합니다. 그것들을 수정하고, 인수로 전달하거나, 함수에서 반환하거나 변수에 할당 할 수 있습니다. 기술적으로 말하면, 그들은 일류 시민 이므로 이름 : 일류 기능.
위의 예제에서 함수가 and에 할당 된 startAt
( anonymous ) 함수를 반환합니다 . 보시다시피 JavaScript는 다른 엔티티 (일류 시민)와 마찬가지로 함수를 취급합니다.closure1
closure2
이름 바인딩 은 변수 (식별자)가 참조 하는 데이터 를 찾는 것 입니다. 바인딩이 해결되는 방법을 결정하는 것이기 때문에 여기서 범위는 실제로 중요합니다.
위의 예에서 :
y
에 바인딩됩니다 3
.startAt
의 범위 x
에 바인딩 1
또는 5
(폐쇄에 따라 다름).익명 함수의 범위 내에서 x
값에 바인딩되지 않으므로 상위 startAt
범위 에서 해석해야합니다 .
으로 위키 백과는 말한다 , 범위는 :
바인딩이 유효한 컴퓨터 프로그램의 영역입니다. 여기서 이름은 엔티티를 참조하는 데 사용될 수 있습니다 .
두 가지 기술이 있습니다.
더 설명은 이 질문을 확인 하고 위키 백과에서보세요 .
위의 예에서 JavaScript가 어휘 범위가 지정된 것을 확인할 수 있습니다. x
이 문제가 해결되면 startAt
소스 코드 (x를 찾는 익명 함수는에 정의 됨 startAt
)를 기반으로 상위 범위 에서 바인딩이 검색되므로 호출 스택을 기반으로하지 않고 함수가 호출 된 방식 (범위).
우리가 전화 할 때 우리의 예에서 startAt
, 그것은 할당됩니다 (일류) 함수를 반환 closure1
하고 closure2
, 따라서 폐쇄가 만들어집니다, 전달 된 변수 때문에 1
그리고 5
내 저장됩니다 startAt
반환에 첨부되어있을 것,의 범위를 ' 익명의 기능. 우리는을 통해 익명 함수를 호출 할 때 closure1
와 closure2
같은 인수 (과 3
)의 값 y
(즉, 그 함수의 매개 변수가로), 그러나 즉시 찾을 수 x
익명 함수의 범위에 구속되지 않는 해상도가 계속 때문에, 제 (어휘) 상부 함수 영역 (폐쇄에 저장 한 것) x
발견은 결합 될 하나 1
또는5
. 이제 우리는 합계에 대한 모든 것을 알고 있으므로 결과를 반환 한 다음 인쇄 할 수 있습니다.
이제 클로저와 그 작동 방식을 이해해야합니다. 이는 JavaScript의 기본 부분입니다.
아, 그리고 당신은 또한 카레 가 무엇인지 배웠습니다 . 여러 매개 변수가있는 하나의 함수를 사용하는 대신 함수 (클로저)를 사용하여 연산의 각 인수를 전달합니다.
클로저 는 함수가 자체 범위 변수, 외부 함수 변수 및 전역 변수에 액세스 할 수있는 JavaScript의 기능입니다.
외부 함수가 반환 된 후에도 클로저는 외부 함수 범위에 액세스 할 수 있습니다. 이는 클로저가 함수가 완료된 후에도 외부 함수의 변수와 인수를 기억하고 액세스 할 수 있음을 의미합니다.
내부 함수는 자체 범위, 외부 함수 범위 및 전역 범위에 정의 된 변수에 액세스 할 수 있습니다. 외부 함수는 자체 범위와 전역 범위에 정의 된 변수에 액세스 할 수 있습니다.
폐쇄의 예 :
var globalValue = 5;
function functOuter() {
var outerFunctionValue = 10;
//Inner function has access to the outer function value
//and the global variables
function functInner() {
var innerFunctionValue = 5;
alert(globalValue + outerFunctionValue + innerFunctionValue);
}
functInner();
}
functOuter();
내부 함수 자체 변수, 외부 함수 변수 및 전역 변수 값의 합계 인 20이 출력됩니다.
정상적인 상황에서 변수는 범위 규칙에 의해 구속됩니다. 지역 변수는 정의 된 함수 내에서만 작동합니다. 폐쇄는 편의상이 규칙을 일시적으로 위반하는 방법입니다.
def n_times(a_thing)
return lambda{|n| a_thing * n}
end
위의 코드 에서 람다 (익명 함수 작성자)가 참조 lambda(|n| a_thing * n}
하기 때문에 클로저 a_thing
가 있습니다.
이제 결과 익명 함수를 함수 변수에 넣습니다.
foo = n_times(4)
foo는 일반적인 범위 지정 규칙을 어 기고 내부적으로 4를 사용하기 시작합니다.
foo.call(3)
12를 반환합니다.
• 클로저는 서브 프로그램 및 그것이 정의 된 참조 환경입니다.
– 서브 프로그램을 프로그램의 임의의 위치에서 호출 할 수있는 경우 참조 환경이 필요합니다.
– 중첩 서브 프로그램을 허용하지 않는 정적 범위 언어에는 클로저가 필요하지 않습니다.
– 폐쇄는 서브 프로그램이 중첩 범위의 변수에 액세스 할 수 있고 어디에서나 호출 할 수있는 경우에만 필요합니다.
– 클로저를 지원하기 위해 구현은 일부 변수에 무제한 범위를 제공해야 할 수 있습니다 (하위 프로그램이 더 이상 존재하지 않는 비 로컬 변수에 액세스 할 수 있기 때문에)
예
function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);
또 다른 실제 사례와 게임에서 인기있는 스크립팅 언어 인 Lua를 사용합니다. stdin을 사용할 수없는 문제를 피하기 위해 라이브러리 함수가 작동하는 방식을 약간 변경해야했습니다.
local old_dofile = dofile
function dofile( filename )
if filename == nil then
error( 'Can not use default of stdin.' )
end
old_dofile( filename )
end
old_dofile의 값은이 코드 블록이 범위를 끝내면 (로컬이기 때문에) 사라지지만 값이 클로저로 묶여 있으므로 새로 정의 된 dofile 함수가 액세스 할 수 있습니다. 또는 함수와 함께 함수와 함께 저장된 사본이 'upvalue'.
에서 Lua.org :
함수가 다른 함수로 묶여 작성된 경우, 함수를 둘러싸는 함수에서 로컬 변수에 완전히 액세스 할 수 있습니다. 이 기능을 어휘 범위 지정이라고합니다. 그것은 분명하게 들릴지 모르지만 그렇지 않습니다. 어휘 범위 및 일류 함수는 프로그래밍 언어의 강력한 개념이지만 해당 개념을 지원하는 언어는 거의 없습니다.
클로저 다른 함수 내에 정의 된 함수가있을 때마다 내부 함수는 외부 함수에 선언 된 변수에 액세스 할 수 있습니다. 클로저는 예제와 함께 가장 잘 설명됩니다. 목록 2-18에서 내부 함수가 외부 범위에서 변수 (variableInOuterFunction)에 액세스 할 수 있음을 알 수 있습니다. 외부 함수의 변수는 내부 함수에 의해 닫혔거나 바인드되었습니다. 따라서 폐쇄라는 용어가 사용됩니다. 개념 자체는 충분히 간단하고 직관적입니다.
Listing 2-18:
function outerFunction(arg) {
var variableInOuterFunction = arg;
function bar() {
console.log(variableInOuterFunction); // Access a variable from the outer scope
}
// Call the local function to demonstrate that it has access to arg
bar();
}
outerFunction('hello closure!'); // logs hello closure!
출처 : http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf
폐쇄를 더 깊이 이해하려면 아래 코드를 살펴보십시오.
for(var i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
}, 1000);
}
여기에 무엇이 출력 될까요? 0,1,2,3,4
그렇지 않을 것이다5,5,5,5,5
폐쇄 때문이
어떻게 해결 될까요? 답변은 다음과 같습니다.
for(var i=0; i< 5; i++){
(function(j){ //using IIFE
setTimeout(function(){
console.log(j);
},1000);
})(i);
}
함수가 생성 된 경우 5 번이라는 첫 번째 코드에서 for 루프를 호출 할 때까지 아무 일도 일어나지 않지만 즉시 호출되지 않았을 때 즉 1 초 후에 호출 할 때도 마찬가지입니다. var i에서 마지막으로 실행 setTimeout
함수를 다섯 번 하고 인쇄하십시오.5,5,5,5,5
IIFE 즉, 즉각적인 호출 함수 표현식을 사용하여 해결하는 방법
(function(j){ //i is passed here
setTimeout(function(){
console.log(j);
},1000);
})(i); //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4
자세한 내용은 실행 컨텍스트를 이해하여 클로저를 이해하십시오.
let (ES6 기능)을 사용 하여이 문제를 해결하는 또 다른 솔루션이 있지만 위의 기능은 작동합니다.
for(let i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}
Output: 0,1,2,3,4
=> 자세한 설명 :
메모리에서 for 루프 실행 그림을 다음과 같이 만드십시오.
루프 1)
setTimeout(function(){
console.log(i);
},1000);
루프 2)
setTimeout(function(){
console.log(i);
},1000);
루프 3)
setTimeout(function(){
console.log(i);
},1000);
루프 4)
setTimeout(function(){
console.log(i);
},1000);
루프 5)
setTimeout(function(){
console.log(i);
},1000);
여기서 나는 실행되지 않고 완전한 루프 후에 var i는 값 5를 메모리에 저장했지만 범위는 항상 자식 함수에서 볼 수 있으므로 함수가 setTimeout
5 번 안에 실행될 때 인쇄됩니다.5,5,5,5,5
위의 설명에 따라이 사용 IIFE를 해결하십시오.
Currying : 인수의 서브 세트 만 전달하여 함수를 부분적으로 평가할 수 있습니다. 이걸 고려하세요:
function multiply (x, y) {
return x * y;
}
const double = multiply.bind(null, 2);
const eight = double(4);
eight == 8;
클로저 : 클로저는 함수 범위 밖에서 변수에 액세스하는 것 이상입니다. 함수 내부의 함수 또는 중첩 함수는 클로저가 아니라는 점을 기억해야합니다. 함수 범위 밖의 변수에 액세스해야 할 때는 항상 클로저가 사용됩니다.
function apple(x){
function google(y,z) {
console.log(x*y);
}
google(7,2);
}
apple(3);
// the answer here will be 21
폐쇄는 매우 쉽습니다. 우리는 다음과 같이 고려할 수 있습니다 : Closure = function + its lexical environment
다음 기능을 고려하십시오.
function init() {
var name = “Mozilla”;
}
위의 경우 폐쇄는 무엇입니까? 어휘 환경의 함수 init () 및 변수, 즉 이름. 폐쇄 = init () + 이름
다른 기능을 고려하십시오.
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
여기서 폐쇄는 무엇입니까? 내부 함수는 외부 함수의 변수에 액세스 할 수 있습니다. displayName ()은 부모 함수 init ()에 선언 된 변수 이름에 액세스 할 수 있습니다. 그러나 displayName ()에서 동일한 로컬 변수가 있으면 사용됩니다.
폐쇄 1 : 초기화 함수 + (이름 변수 + displayName () 함수)-> 어휘 범위
클로저 2 : displayName 함수 + (이름 변수)-> 어휘 범위
프로그래밍 상태는 단순히 기억하는 것을 의미합니다.
예
var a = 0;
a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3
위의 경우 상태는 변수 "a"에 저장됩니다. 우리는 1을 "a"에 여러 번 추가합니다. 우리는 그 가치를 "기억"할 수 있기 때문에 그렇게 할 수 있습니다. 상태 홀더 "a"는 해당 값을 메모리에 보유합니다.
종종 프로그래밍 언어에서는 사물을 추적하고 정보를 기억하며 나중에 액세스하려고합니다.
이것은 다른 언어 로 클래스를 사용하여 일반적으로 수행됩니다. 변수와 마찬가지로 클래스는 상태를 추적합니다. 그리고 그 클래스의 인스턴스는 차례로 그 안에 상태를 갖습니다. 상태는 단순히 나중에 저장하고 검색 할 수있는 정보를 의미합니다.
예
class Bread {
constructor (weight) {
this.weight = weight;
}
render () {
return `My weight is ${this.weight}!`;
}
}
"렌더링"방법에서 "무게"에 어떻게 액세스 할 수 있습니까? 주정부 덕분에 Bread 클래스의 각 인스턴스는 해당 정보를 저장할 수있는 메모리의 "상태"에서 읽음으로써 자체 가중치를 렌더링 할 수 있습니다.
이제 JavaScript는 역사적으로 클래스가없는 매우 독특한 언어 입니다 (현재는 있지만 기능과 변수 만 있습니다). 클로저는 JavaScript가 사물을 기억하고 나중에 액세스 할 수있는 방법을 제공합니다.
예
var n = 0;
var count = function () {
n = n + 1;
return n;
};
count(); // # 1
count(); // # 2
count(); // # 3
위의 예는 변수를 사용하여 "상태 유지"의 목표를 달성했습니다. 대단해! 그러나 이것은 변수 ( "상태"홀더)가 이제 노출된다는 단점이 있습니다. 우리는 더 잘할 수 있습니다. 클로저를 사용할 수 있습니다.
예
var countGenerator = function () {
var n = 0;
var count = function () {
n = n + 1;
return n;
};
return count;
};
var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3
이제 "count"기능을 셀 수 있습니다. 상태를 "유지"할 수 있기 때문에 가능합니다. 이 경우의 상태는 변수 "n"입니다. 이 변수는 이제 닫힙니다. 시간과 공간에서 휴무입니다. 시간이 지나면 복구, 변경, 가치 할당 또는 직접 상호 작용할 수 없기 때문입니다. "countGenerator"함수 내에 지리적으로 중첩되어 있기 때문에 공간에 있습니다.
왜 이것이 환상적입니까? 다른 정교하고 복잡한 도구 (예 : 클래스, 메서드, 인스턴스 등)를 사용하지 않아도 1. 멀리서 제어 할 수 있습니다.
상태 인 변수 "n"을 숨기면 프라이빗 변수가됩니다! 또한이 변수를 미리 정의 된 방식으로 제어 할 수있는 API를 만들었습니다. 특히 "count ()"와 같이 API를 호출 할 수 있으며 "거리"에서 "n"에 1을 추가합니다. 어떤 식 으로든 API를 제외하고 누구나 모양이나 형태를 "n"에 액세스 할 수 없습니다.
폐쇄는 이것이 왜 큰 부분입니다.