다음은 버그가 존재하는 코드이다.
function wrapElements(a){
var result = [] ,i ,n;
for(i = 0, n = a.length; i<n;i++){
result [i] = function(){ return a[i]; };
}
return result;
}
var warpped = wrapElements([10,20,30,40,50]);
var f = wrapped[0];
f(); //
다음 코드에서 10 이라는 값을 계산할 의도로 코드를 작성했겠지만, 실제로는 undefined 값이 만들어진다.
이 예제를 제대로 동작하도록 하기 위해서 바인딩과 할당의 차이점을 이해해야 한다. 런타임시 스코프에 진입하면 해당 스코프에 있는 변수들을 바인딩하기 위해 메모리에 ‘슬롯’
을 할당한다.
wrapElements
함수는 세 지역 변수 result
, i
, n
을 바이딩한다. 따라서 이 함수가 호출되면 wrapElements
함수는 이 세 변수들을 위한 슬롯을 할당한다. 반복문을 순회할 때마다, 반복문의 본문은 감싸는 함수를 위한 클로저를 할당한다.
우리의 실수는 함수가 생성되는 시점에 그 함수가 i의 값을 명백히 저장하고 있다고 기대하기 때문에 발생한다. 클로저는 외부 변수의 값
이 아니라 참조
를 저장한다.
클로저 i를 실제로 호출할 때에는 배열의 다섯번째 인덱스를 찾게되어 결과적으로 undefined
를 반환하게된다.
function wrapElements(a){
var result = [];
for (var i = 0, n = a.length;i < n ; i++){
result[i] = function(){return a[i]};
}
return result;
}
var warpped = wrapElements([10,20,30,40,50]);
var f = wrapped[0];
f(); //undefined
변수는 맨 윗부분으로 호이스팅 되기 때문에 i를 위한 하나의 슬롯만 할당이된다.
function wrapElements(a){
var result = [];
for (var i = 0, n = a.length;i < n ; i++){
(function(){
var j = i;
result[i] = function(){return a[i]};
})();
}
return result;
}
var warpped = wrapElements([10,20,30,40,50]);
var f = wrapped[0];
f(); //undefined
다음과 같이 감싸진 함수를 만들어 강제로 지역 스코프를 만들고 즉시 실행하는 방법으로 이 문제를 해결할 수 있다.
이 방법은 즉시 실행함수 표현식 또는 IIFE(immediately invoked function expression)
라고 부르며 블록스코프를 지원을 위한 필수적인 차선책이다.
function wrapElements(a){
var result = [];
for (var i = 0, n = a.length;i < n ; i++){
(function(j){
result[i] = function(){return a[j]};
})(i);
}
return result;
}
지역스코프를 생성하기 위해 IIFE를 사용할 때는, 함수 안에 블록으로 감싸는 것이 블록에 어떤 이상한 변화를 만들기 때문에 조힘해야 한다.
break
나 continue
를 사용할 수 없다. arguments
변수를 참조하면, IIFE
는 이를 다르게 해석한다. 혹은 function안에 적어주면 해결된다.
function wrapElements(a){
let result = [];
for (let i = 0, n = a.length;i < n ; i++){
result[i] = function(){
var j = i
return a[j]
};
}
return result;
}
var wrapped = wrapElements([10,20,30,40,50]);
var f = wrapped[0];
f()