2. 먼저,
지역 변수의 유효 범위는 블럭이 아닌 함수!
function foo() {
var bar = ‘bar’;
console.log(bar); //--> ‘bar’
if (true) {
var bar = ‘baz’;
console.log(bar); //--> ‘baz’
}
console.log(bar); //--> ‘baz’
}
3. 즉시 실행 함수로 유효 범위를 제한할 수 있음.
function foo() {
var bar = ‘bar’;
console.log(bar); //--> ‘bar’
if (true) {
(function () {
var bar = ‘baz’;
console.log(bar); //--> ‘baz’
}());
}
console.log(bar); //--> ‘bar’
}
5. 용어가 좀 헷갈릴 수 있다.
변수 선언(Variable Declaration)
var foo = ‘bar’;
함수 선언(Function Declaration)
function foo() {}
함수 표현식(Function Expression)
var foo = function () {};
(function foo() {});
6. 변수 선언인 경우
function foo() {
console.log(bar); //--> undefined
var bar = 'baz';
console.log(bar); //--> 'baz'
}
아래와 동일
function foo() {
var bar;
// 변수 선언은 함수의 맨 앞으로 끌어올려진다.(hoisted)
console.log(bar);
bar = 'baz'; // 할당은 실행 시점에 수행
console.log(bar);
}
7. for 문의 경우도 동일
function foo(arr) {
for (var i = 0, len = arr.length; i < len; i++) {
console.log(arr[i]);
}
}
아래와 동일
function foo(arr) {
var i, len;
for (i = 0, len = arr.length; i < len; i++) {
console.log(arr[i]);
}
}
8. 함수 정의인 경우
function foo() {
bar(); //--> 'bar'
function bar() {
console.log('bar');
}
}
아래와 동일
function foo() {
function bar() {
console.log('bar');
}
bar();
}
9. 함수 표현식인 경우 (변수 선언과 동일)
function foo() {
bar(); //--> ReferenceError
var bar = function () {};
bar(); // 호출되지 않음
}
아래와 동일
function foo() {
var bar;
bar();
bar = function () {};
bar();
}
10. 변수 선언은 호이스팅으로 맨 앞으로,
변수 할당은 실행시점에 수행됨.
호이스팅으로 헷갈릴 수 있으니,
함수의 맨 앞 부분에 한 개의 var만 쓸 것을 권장.
function foo() {
var a = 1,
b = 2,
c = 3;
}
11. 그치만, 눈을 크게 떠야할 때가 있음.
function foo() {
var a = 1,
b = 3,
c = 4;
d = 2;
}
오류 없이 실행되며, 변수 d는 전역으로 선언됨.
린트 툴을 사용할 것을 권장.
12. 디버깅이 어려운 경우도 있음.
function foo() {
var a = 1,
b = getComplexValue(a), // 브레이크 포인트를 잡기 어려움
c = b + 3;
}
상황에 따라 var로 나누는 것도 나쁘지 않은 선택.
function foo() {
var a = 1;
var b = getComplexValue(a);
var c = b + 3;
}
13. 또한, 분기문 내에 함수 정의를 넣지 말 것.
function foo(condition) {
if (condition) {
function bar() {
console.log('first bar');
}
} else {
function bar() {
console.log('second bar');
}
}
bar();
}
foo(true); //--> 'second bar'
foo(false); //--> 'second bar'
14. 함수 정의 호이스팅에 따라 아래와 같기 때문.
function foo(condition) {
function bar() {
console.log('first bar');
}
function bar() {
console.log('second bar');
}
if (condition) {
} else {
}
bar();
}
* ES5 strict 모드에서는 SyntaxError 발생
15. 상황에 따라 동적으로 할당하고 싶다면.
function foo(condition) {
var bar;
if (condition) {
bar = function () {
console.log('first bar');
};
} else {
bar = function () {
console.log('second bar');
};
}
bar();
}
foo(true); //--> 'first bar'
foo(false); //--> 'second bar'
16. 개인적으로는, var는 맨 위에,
함수는 읽어내려가기 편하게 작성하는 것을 선호함.
function foo() {
var a, b, c;
doAll();
function doAll() {
doA();
doB();
}
function doA() {}
function doB() {}
}
17. 하지만 리턴문 뒤의 함수 정의는,
문맥 상 그다지 좋은 것 같지 않음.
function foo() {
return bar();
function bar() {
return ‘baz’;
}
}
* 함수 정의 호이스팅으로 문제 없이 실행됨.
19. 부록. 좀 더 자세히 알아보면.
function test(a, b) {
console.log(a, b, c, d);
var c = 10;
function d() {}
}
test(10);
위와 같은 함수가 전역 범위에서 실행될 때.
20. 다음과 같은 식으로 실행됨.
1. foo()가 호출되면,
2. foo()의 실행 컨텍스트가 생성되고,
3. 실행 컨텍스트는 함수를 실행하기 전 활성화 객체를 생성한다.
4. 활성화 객체에는 파라미터 정보, 변수 선언, 함수 정의가 포함된다.
5. 함수가 실행되고 변수를 만났을 때엔,
유효범위 체인에 따라 활성화 객체에서 먼저 찾고, 상위 탐색을 진행한다.
21. 이 때, 활성화 객체에는 다음 값들이 할당된다.
- 파라미터로 전달된 값
파라미터의 이름과 값을 할당
없는 경우 undefined로 할당
- 모든 함수 선언
함수의 이름과 정의를 할당
이미 정의되어 있는 경우 덮어씀
- 모든 변수 선언
변수의 이름과 undefined를 할당
이미 정의되어 있는 경우 할당하지 않음
22. 즉, 아래 코드가 실행되기 전,
function test(a, b) {
console.log(a, b, c, d);
var c = 10;
function d() {}
}
test(10);
활성화 객체(Activation Object)는 다음과 같이 생성된다.
AO = {
a: 10,
b: undefined,
c: undefined,
d: <reference to function ‘d’>
};
23. 실행 시점에서 변수를 만났을 때엔,
활성화 객체의 값을 찾거나 설정한다.
function test(a, b) {
console.log(a, b, c, d);
// 각각 AO[‘a’], AO[‘b’], AO[‘c’], AO[‘d’]의 값
// 10, undefined, undefined, function
var c = 10; // AO[‘c’] = 10; 과 동일하다.
function d() {}
}
test(10);
24. 함수 표현식은 활성화 객체에 할당되지 않는다.
function test() {
var foo = function bar() {};
// foo는 AO에 존재하지만, bar는 존재하지 않는다.
// bar의 접근을 시도하면 ReferenceError가 발생한다.
(function baz() {});
// 마찬가지로 baz도 AO에 할당되지 않는다.
}
25. 호이스팅은 활성화 객체의 할당 방식에 따른 현상.
function foo() {
console.log(bar); //--> function ‘bar’
// AO[‘bar’] = <reference to function ‘bar’>
var bar = 10;
function bar() {};
var bar = 20;
}