Closure part1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//결과로 함수를 리턴 하는 함수
function greet(whattosay) {
return function(name) {
console.log(whattosay + ' ' + name);
}
}

greet('Hi')('Tony');
//Hi Tony

// greet() 실행이 끝나면 greet()의 execution context 사라짐
var sayHi = greet('Hi');

// 그럼에도 불구하고 어떻게 sayHi()가 다시 whattosay를 참조 했을까?
sayHi('Tony');
//Hi tony

Closure, 클로저 ?

greet(‘Hi’) 가 실행되면 execution context를 생성하고, whattosay 변수를 생성한다.

greet() 코드 실행이 끝나면 greet() 의 execution context 는 삭제되지만 whattosay 는 메모리에 여전히 남아있다.

그 다음 sayHi(‘Tony’) 가 실행되면 execution context, name 변수 생성하고 다시 한 번 greet() 의 코드를 실행할 것이다. 그러다 console.log(whattosay + ‘ ‘ + name); 코드를 다시 만나면 sayHi(“Tony”)의 execution context 에서 whattosay 변수를 탐색하고, 없다면 스코프체인을 따라 outer environment에서 whattosay를 다시 탐색, 코드를 실행한다.

greet() 함수가 종료되었음에도 내부의 function(name) { console.log(whattosay + ‘ ‘ + name) }; 은 여전히 외부의 whattosay에 접근할 수 있는 셈이다.

다시말해, 외부함수가 종료된 뒤에도 내부함수는 여전히 외부함수의 execution context가 사용한 메모리공간에 접근이 가능하다는 것이다. 이러한 특성을 Closure, 클로저라고 한다. 자바스크립트에서는 이 클로저의 특성을 활용한 패턴이 매우 빈번하게 활용되고 있으니 꼭 그 원리를 이해하고 넘어가자.

클로저 예제

아래의 예제를 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function buildFunction() {
var arr = [];

for (var i = 0; i <3; i++) {
arr.push(
function() {
console.log(i);
}
)
}
return arr;
}

var fs = buildFunction();

fs[0]();
fs[1]();
fs[2]();

// 3 3 3 (!?!? 왜때문에?)

코드를 잘 살펴보자

buildFunction() 이 먼저 실행되면 노란색으로 표시한 for문 내부의 로직은 expression 이 아니라 statement기 때문에 실행되진 않고 함수선언 자체가 배열에 각각 삽입 될 것이다.

buildFunction() 실행이 끝나면 exectuion context 는 삭제되지만 해당 변수들은 메모리에 남아있다.

이후 fs[0,1,2] 함수가 각각 실행되어 내부로직을 실행할 때 클로저 특성에 따라 arr과 i를 참조하게 된다. fs() 를 호출하는 시점에서 i의 값은 3이기 때문에 3번의 실행 모두 i=3 을 참조 해서 0,1,2 가 아닌 3,3,3을 출력하게 된다.

###만약 0,1,2를 출력하게 하려면?

let keyword

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function buildFunction2() {
var arr = [];

for (var i = 0; i <3; i++) {
let j = i; //block 내 한정, 함수 호출마다 다른 i 삽입
arr.push(
function() {
console.log(j);
}
)
}
return arr;
}

var fs = buildFunction();

fs[0]();
fs[1]();
fs[2]();

IIFE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function buildFunction3() {
var arr = [];

for (var i = 0; i <3; i++) {
arr.push(
(function(j) {
return function() {
console.log(j);
}
})(i);
//IIFE로 각각의 ec 생성, i를 변수j에 저장하면
//내부함수 실행마다 closure로 각각의
//i값 저장한 변수j에 접근가능 => 0,1,2
)

}

return arr;
}

var fs = buildFunction();

fs[0]();
fs[1]();
fs[2]();

각각의 execution context 를 생성하고 그때마다 i를 따로 저장하면 가능한데 ,execution context를 생성하는 유일한 방법은 함수를 실행하는 것이다.. -> IIFE 활용


Closure part2

IIFE

Share