[Effective JavaScript] 즉시실행 함수 표현식으로 지역변수 만들기

김범식·2023년 6월 21일
0

Effective JavaScript

목록 보기
13/33

다음은 버그가 존재하는 코드이다.

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를 사용할 때는, 함수 안에 블록으로 감싸는 것이 블록에 어떤 이상한 변화를 만들기 때문에 조힘해야 한다.

  1. 블록 안에서는 블록 밖으로 나가기 위해 breakcontinue를 사용할 수 없다.
  2. 블록에서 this나 특별한 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()

기억할 점

  • 바인딩과 할당의 차이점을 이해하라
  • 클로저는 외부 변수의 값이 아닌 참조를 저장한다.
  • 지역 스코프를 만들기 위해 즉시 실행 함수 표현식을 사용하라
  • IIFE에서 블록으로 감쌌을 때 변화하는 상황에 주의하라![]
profile
frontend developer

0개의 댓글