1. 글에 대해
- 모든 결과는 Chrome 브라우저를 통해 테스트된 결과입니다.
2. ES5 & var
-
var 키워드를 통한 변수 선언
-
ES5 는 오직 function execution context 내부에서만 Isolated Scope(격리된 유효범위) 를 가질 수 있다.
// global ec var a = 0; function A(){ // A function ec // AFunctionEC.AO.a var a = 1; (function B(){ // B function ec var a = 2; // BFunctionEC.AO.a console.log(a); // 2 })() console.log(a); // 1 } // globalEC.VO.a console.log(a); // 0 A();
-
즉 ES5 는 클래스 기반 언어인 C or C++ 처럼, 블럭 내부에 Isolated Scope(격리된 유효범위) 를 가질 수 없다.
// global ec var a = 0; if (true){ // if 문 블럭 내부의 local context 를 갖지않는다. // 즉 선언된 변수 a 는 다른 값(1)으로 대치된다. var a = 1; } // globalEC.VO.a console.log(a); // 1
-
3. ES6 & let
-
let 키워드를 통해 선언된 변수는, 블럭 범위의 Isolated Scope(격리된 유효범위) 를 갖게된다.
// global scope { // block scope // let 키워드를 통한 변수 선언 let a = 1; } if (true){ let b = 2; } switch (1){ case 1: let c = 3; break; } for (let d = 0; d < 3; d++){ console.log(d); // 0, 1, 2 } let e = 4; try { // 실행 코드 처리 시, 블록 범위에 선언된 a 변수는 선언되지 않았다는 예외가 발생하게 된다. console.log(a); } catch(e){ // a is not defined console.log(e.message); } try { // 실행 코드 처리 시, 블록 범위에 선언된 b 변수는 선언되지 않았다는 예외가 발생하게 된다. console.log(b); } catch(e){ // b is not defined console.log(e.message); } try { // 실행 코드 처리 시, 블록 범위에 선언된 c 변수는 선언되지 않았다는 예외가 발생하게 된다. console.log(c); } catch(e){ // c is not defined console.log(e.message); } try { // 실행 코드 처리 시, 블록 범위에 선언된 d 변수는 선언되지 않았다는 예외가 발생하게 된다. console.log(d); } catch(e){ // d is not defined console.log(e.message); } console.log(e); // 4
-
let 키워드를 통해 선언된 변수의 유효 범위는 자신을 포함한 블럭 및 그 내부 블럭까지 유효하다.
{ let a = 1; { let b = 2; { let c = 3; { let d = 4; // 선언된 변수의 유효 범위는 자신을 포함한 블럭 및 그 내부 블럭까지 유효하다 console.log(a, b, c, d); // 1, 2, 3, 4 } } } console.log(a); // 1; // 해당 블럭 Scope 에서는 b 변수에 접근할 수 없다. try{ console.log(b); } catch(e){ // b is not defined console.log(e.message); } }
-
동일 블럭 범위에서, 동일한 식별자 이름으로 변수를 재 선언시 예외가 발생하게된다.
// 동일 블럭 범위에서, 동일한 식별자 이름으로 변수를 재 선언시, 아래와 같은 예외가 발생하게된다. let a; // Uncaught SyntaxError: Identifier 'a' has already been declared let a; { let b; // Uncaught SyntaxError: Identifier 'b' has already been declared let b; } if (true){ let i; let i; // Uncaught SyntaxError: Identifier 'i' has already been declared } (function A(){ let c; let c; // Uncaught SyntaxError: Identifier 'c' has already been declared })(); // 다른 식별자 이름으로 선언 시 예외가 발생하지 않는다. { let c; let d; console.log(d); // undefined }
-
let 키워드를 통해 선언된 변수는 EC 진입 및 실행 코드 처리 후에도 VO 의 새로운 속성으로 추가되지않는다.
-
변수(a)가 Scope 에는 존재하지만, 초기화 되지않는 이 단계를 가리켜 Temporal Dead Zone(일시적 사각 지대)라고 부르며, 관련 예외 또한 모두 구현되어있다.
// global ec 진입 시 VO 의 새로운 속성으로 추가되지않는다. try { console.log(a); // a is not defined } catch(e){ console.log(e.message); } let a = 1; // 할당된 값을 반환한다. console.log(a); // 1 // 실행 코드 처리후에도 VO 의 새로운 속성으로 추가되지않는다. console.log(window.a); // undefined
-
-
동일 VO(AO) 범위에서, 동일한 식별자 이름으로 변수(or 함수) 선언 시 예외가 발생하게 된다.(단 let 키워드를 통해 선언된 변수는 VO 의 속성으로 추가되지않는다)
// 테스트 1 // global ec let a; // globalEC.VO.a var a; // Uncaught SyntaxError: Identifier 'a' has already been declared // 테스트 2 // global ec let a; { // globalEC.VO.a var a; // Uncaught SyntaxError: Identifier 'a' has already been declared } // 테스트 3 // global ec let a; if (true){ // globalEC.VO.a var a; // Uncaught SyntaxError: Identifier 'a' has already been declared } // 테스트 4 // global ec let a; // globalEC.VO.a // Uncaught SyntaxError: Identifier 'a' has already been declared function a(){ // a function ec } // 테스트 5 // global ec let a; (function A(){ // A function ec // 이 경우 a 변수는 globalEC.VO 의 속성으로 추가되며, 마찬가지로 예외가 발생하게된다. a; // Uncaught SyntaxError: Identifier 'a' has already been declared })(); // 테스트 6 // global ec (function A(){ // function ec let a; // 이 경우 a 변수는 AFunctionEC.AO 의 속성으로 추가되며, 마찬가지로 예외가 발생하게된다. var a; })(); // 테스트 7 // global ec let a; (function A(){ // A function ec // 이 경우 a 변수는 AFunctionEC.AO 의 속성으로 추가되며, 예외가 발생하지않는다. var a; console.log(a); // undefined })();
-
루프(for, while 등)문 내부에서 (let 키워드를 통해)선언된 변수는, 비동기 함수안에서 각기 다른 변수를 참조하게된다.(즉 각 루프는 서로 다른 변수(값)를 가지게 된다)
// global scope // 루프 문 밖에서 let 키워드를 통해 선언된 변수 let i = 0; for (;i < 10; i++) { // block scope window.setTimeout(function() { // 이 경우 i 변수는 최종 증감값인 10 을 반환하게된다. console.log(i); // 10 }); } // global scope // 루프문 내부에 let 키워드를 통해 선언된 변수 for (let i = 0; i < 10; i++) { // block scope window.setTimeout(function() { // 이 경우 i 변수는 각기 다른 변수 값을 참조하게된다. console.log(i); // 0 ~ 9 }); } // global scope for (var i = 0; i < 10; i++) { // block scope // 루프문 내부에 let 키워드를 통해 선언된 변수 let _i = i; window.setTimeout(function() { // 이 경우 _i 변수는 각기 다른 변수 값을 참조하게된다. console.log(_i); // 0 ~ 9 // var 키워드를 통해 선언된 i 변수는, 함수 Scope Chain 매커니즘에 의해 최종 증감값인 10을 참조하게된다. console.log(i); // 10 }); } console.log(i); // 10; var i = 4; while (i) { // block scope let _i = i; // let 키워드를 통해 변수를 선언한다. window.setTimeout(function() { // 이 경우 _i 변수는 각기 다른 변수 값을 참조하게된다. console.log(_i); // 4 ~ 1 }); i--; } // 위 결과를 ES5 를 통해 구현한 방법은 아래와 같다. for (var i = 0; i < 10; i++){ window.setTimeout(function(i){ // Function.AO.i // bind 함수를 통해 바인딩된 AO.i 값을 참조하게 된다. console.log(i); // 0 ~ 9 }.bind(null, i)); } // 최종 증감값인 10을 반환하게된다. console.log(i); // 10
-
eval 함수를 통해, 선언된 변수(or 함수)는 local sandbox 안에서 평가된다.
eval('let i'); try{ i; } catch(e){ console.log(e.message); // i is not defined }