소개
이 기사에서는 JavaScript의 클로저에 대해 알아 봅니다.
모든 자바 스크립트 개발자는 가장 자주 묻는 인터뷰 질문이기 때문에 클로저를 알고 있어야 할 뿐만 아니라 명확하게 이해하면 코드에서 버그가 발생하지 않도록 하는 데 도움이 됩니다.
그럼 시작하겠습니다.
클로저란 무엇입니까?
클로저에 들어가기 전에 먼저 JavaScript에서 범위가 작동하는 방식을 이해해야 합니다.
아래 코드를 살펴보십시오.
function display() {
var i = 10;
console.log(i); // 10
}
display();
console.log(i); // Uncaught ReferenceError: i is not defined
함수 내에서 var / let / const 키워드로 변수를 선언하면 해당 함수 내에서만 사용할 수 있습니다. 즉, 전용 변수가 되기 때문에 해당 함수로 범위가 지정된다는 의미입니다.
위의 코드를 실행하면 함수 내에서 console.log가 함수 범위 내에서 변수 i를 사용할 수 있으므로 값 10을 인쇄하는 것을 볼 수 있습니다.
그러나 함수 외부에는 변수가 존재하지 않으므로 인쇄하려고 하면 Uncaught ReferenceError가 표시됩니다.
이제 아래 코드를 살펴보십시오.
function add(x) {
var sum = 5;
function result(y) {
sum = sum + (x * y);
return sum;
}
return result;
}
var output = add(4); // output will contain the result function
console.log(output(5)); // 25
console.log(output(10)); // 65
위의 코드에서 우리는 add 함수 안에 result 함수를 정의했으며 add 함수에서 result 함수를 반환합니다.
따라서 첫 번째 console.log에서는 합계가 함수 내에서 5로 초기화되어 5 + (4 * 5) = 25이므로 출력으로 25를 얻습니다.
10을 전달하여 출력 변수에 저장된 함수를 다시 호출하면 sum 함수가 실행을 마쳤더라도 마지막으로 계산 된 sum 변수의 값인 25가 유지되므로 출력은 25 + (4 * 10) = 65.
여기에 클로저의 정의가 있습니다.
다른 함수 내에 정의되고 외부 함수에서 반환 된 함수는 외부 함수가 종료 된 후에도 내부 함수가 정의되었을 때 범위에 있던 모든 변수에 대한 액세스 권한을 유지합니다.
간단히 말해서, 위 코드에서 결과 함수를 정의 할 때 sum 변수에 액세스 할 수 있고 add 함수에서 결과 함수를 반환하므로 결과 함수는 add 함수 후에도 sum 변수에 대한 액세스 권한을 유지합니다. 실행이 완료되었습니다.
이것을 클로저라고합니다.
클로저는 함수 내부에서만 액세스 할 수 있고 함수 호출에서 그 값을 유지하는 개인 변수를 만드는 방법을 제공하므로 매우 유용합니다.
따라서 sum 변수에 직접 액세스 할 수 없으며 sum 변수의 오용을 방지하는 결과 함수 호출을 통해서만 액세스 할 수 있습니다.
아래 코드를 살펴보십시오.
function getCharacter(name) {
let i = 0;
return function next() {
const value = name[i++];
return value;
}
}
const next = getCharacter('hello');
console.log(next()); // h
console.log(next()); // e
console.log(next()); // l
console.log(next()); // l
console.log(next()); // o
위의 코드에서 변수 i는 여러 함수 호출에서 값을 유지하므로 클로저를 사용하여 모든 함수 호출에서 각 문자를 가져올 수 있습니다.
위 코드를 아래와 같이 다시 작성할 수 있습니다.
function getCharacter(name) {
let i = 0;
return {
next: function() {
const value = name[i++];
return value;
}
};
}
const obj = getCharacter('hello');
console.log(obj.next()); // h
console.log(obj.next()); // e
console.log(obj.next()); // l
console.log(obj.next()); // l
console.log(obj.next()); // o
위의 코드에서 우리는 함수로부터 객체를 반환하고 객체는 속성으로서의 기능을 가지고 있으므로 또한 클로저가 될 것입니다.
이제 아래 코드를 살펴보십시오.
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
이것은 매우 유명한 인터뷰 질문입니다.
위의 코드가 0에서 4까지의 숫자를 인쇄한다고 생각할 수 있지만 실제로는 숫자 5를 다섯 번 인쇄합니다.
이는 for 루프 내에서 setTimeout 함수를 사용했기 때문에 시간이 지나면 setTimeout이 실행되고 (1 초 후) for 루프가 이미 실행을 마쳤기 때문입니다.
따라서 for 루프가 끝났을 때 i의 값은 5이므로 setTimeout 함수는 값 5를 5 번 인쇄합니다.
이 문제를 해결할 수 있는 방법에는 두 가지가 있습니다.
for(var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000)
})(i);
}
위의 코드는 0에서 4까지의 값을 올바르게 인쇄합니다.
함수에서 setTimeout 호출을 래핑하여 각 상호 작용에 대해 고유 한 범위를 생성하기 때문입니다.
따라서 (i)에서 내부 함수에 전달하는 값은 함수 (i) 매개 변수로 함수에 전달됩니다.
ES6는 루프 내에서 사용될 때 각 반복에 대한 새 범위를 생성하는 let 키워드를 추가했습니다. 따라서 0에서 4까지의 값을 올바르게 인쇄하는 var 대신 let 키워드를 사용할 수 있습니다.
for(let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
이제 아래 코드를 살펴보십시오.
var numbers = {};
for(var i = 0; i < 10; i++){
numbers[i] = function() {
console.log(i);
}
}
console.log(numbers[0]()); // 10
console.log(numbers[1]()); // 10
console.log(numbers[2]()); // 10
여기에서도 변수 i의 실제 값 대신 매번 값 10이 인쇄됩니다.
두 가지 방법으로 이 문제를 해결할 수 있습니다.
var numbers = {};
function log(value) {
return function getValue() {
return value;
}
}
for(var i = 0; i < 10; i++){
numbers[i] = log(i);
}
console.log(numbers[0]()); // 0
console.log(numbers[1]()); // 1
console.log(numbers[2]()); // 2
위의 코드에서 우리는 로그 함수 안에 getValue 함수를 정의 했으므로 전달 된 i 값을 유지할 클로저를 만들었습니다.
따라서 우리는 올바른 예상 출력을 얻습니다.
결론
이 기사에 대한 내용입니다.
이제 JavaScript에서 클로저를 사용하는 것이 명확 해 졌기를 바랍니다.
https://dev.to/myogeshchavan97/understanding-closures-in-javascript-1nnp
등록된 댓글이 없습니다.