난 정말 최고야 멋있어
ydkjsy-scope-closures Chapter 5 본문
When Can I Use a Variable?
언제 변수들을 사용할 수 있을까요? 만들어진 이후라고요?
예제를 한번 봐볼게영
greeting(); // Hello 가 출력됨
function greeting(){
console.log("Hello");
}
ㅇㅇ 보시면 알겠지만 생성직후에 변수들이 생기는게 아님
컴파일 타임에 이미 변수들의 스코프에 관한 식별자들이 입력됨
또한 모든 식별자들은 해당하는 스코프에 들어갈때마다 생성됨
대충 이런걸 호이스팅이라고 함
근데 호이스팅이 완벽한 정답은 아님 ㅇㅇ
어떻게 선언전에 호출이 되는걸까. 즉 어캐 스코프가 시작하자마자 greeting 이라는 변수에 값이 들어있는것일까
그건 function 선언의 특별한 성질땜에 그럼 (그걸 함수 호이스팅이라고 함)
함수선언의 경우엔 이름 선언만 할뿐아니라 함수의 레퍼런스에 대해서 초기화까지 해줌
그래서 함수가 스코프 전반에서 호출 될 수 있는것임
좀더 세세한것까지 파고들자면 function 호이스팅과 var변수 호이스팅은 function
스코프를 따름 (=블록스코프 아니라구)
*Note
let이랑 const 도 호이스팅은 됨 근데 TDZ 가 막는거임 ㅋㅋ *
- Hoisting: Declaration vs Expression
fucntion 호이스팅은 함수 선언에만 해당됨 표현식에는 해당 안됨
greeting(); //에러 (TypeError)
var greeting = function greeting(){
console.log("Hello!");
};
ㅇㅇ 이건 에러띄움 표현식이라서
근데 중요한건 Type Error 라는 거임 => 허용되지 않은 값에 뭔갈 하려고 했단 소리
주목할건 이 에러가 레퍼런스 에러는 아니란거임 즉 JS 가 스코프에서 greeting 이라는 녀석을 못찾겠다는 소리는 아님
greeting 이라는 변수는 찾았찌만 그때 그순간엔 함수에 대한 레퍼런스를 갖고 있찌 않았다는 거임
함수듦나 호출될수 있고 non-function 을 호출하면 당연히 에러를 뜨지
근데 글면 greeting은 함수레퍼런스가 아닌 뭐를 갖고 있었을까
호이스팅되는것에 더해 var 로 선언된 친구들은 스코프가 시작될때 자동으로 undefined 로 초기화됨
그니까 undefined 갖고 있었겠찌
여기서 차이에 잠시 주목! function declaration 함수 선언은 호이스팅과 함께 함수값을 초기화함 (function hoisting)
글고 var 도 역시 호이스팅은 일어나지만 undefined로 초기화됨
두경우 식별자(greeting) 은 호이스팅됨 근데 ㅇㅇㅇㅇ
같은소리만 계속 몇번말하니!!!
- VariableHoisting
또다른 예쩨를 봐보장
greeting = "Hello!";
console.log(greeting); //Hello!
var greeting = "Howdy!";
첫째줄에서 선언도 안됬는데 초기화가 된 이유는 몰까??
- 호이스팅이 되어서
- 자동으로 undefined 로 초기화가 되었기 때문에
이 두가지가 항상 있어야함
이제 중언 부언 그만하자 ㅋㅋ
Hoisting : Yet Another Metaphor
호이스팅은 JS 엔진이 실행하는 특정 단계라기보다
프로그램이 실행전에 JS 가 다양한 액션들을 시각화 하는거라고 생각하면 편함
'호이스팅은 리프팅이다' 와!!
맨위로 올려서 프로그램을 다시 짠다고 생각해!!!
var greeting;
greeting = "Hello!";
console.log(greeting);
greeting = "Howdy!";
ㅇㅇ 글고 이 비유는 함수 선언에도 적용잘됨 그러니 날 괴롭히지마 ㅠㅠㅠㅠ
대충 이 호이스팅 비유는 아주 편리합니당
일케 생각하면 싱글패스(single pass)로 js 엔진이 실행된다고 생각할수 있어여
그 챕터 1에서 투페이즈 프로세싱과 반대로 생각하세여 (그 스코프 매니저랑 누구랑 어쩌고랑 저쩌고랑 한거)
호이스팅을 코드 순서의 재정렬로 이해하는것은 꽤나 매력적인 단순화지만 정확하진 않음 ㅋㅋ
진짜 찾는 방법은 그냥 스코프 바운더리 다돌면서 뺑뺑이 돌리는거임
파싱이 뭐냐고?? 알골있음 ㅁ패스
부정확해도 이건 TC39도 애용할정도로 유용함 ㅇㅇㅇㅇㅇㅇ
근데 적어도 호이스팅이 정말 코드를 재배치한다고 생각하진 말자구
호이스팅은 컴파일 타임에 컴파일타임에 컴파일 타임에 일어나!!!
스코프에 들갈때마다 런타임타임에 자동으로 등록을 하는 변수들을 처음에 만들어!!!
Re-declaration?
재선언이 될때 어캐될지 생각해본적있니??
var studentName = "Frank";
console.log(studentName);
//Frank
var studentName;
console.log(studentName);
두번째 로그에선 뭐가 찍힐까
보통 재선언되서 undefined 가 찍힐거라고 생각하지만 아냐
안됨 ㅋㅋ 그냥 Frank 임
그 비유로 한번 생각해보자
var studentName;
var studentName; // 응 안돼
studentName = "Frank";
console.log(studentName);
console.log(studentName);
ㅇㅇ 그래서 중간에서 선언하는건 사실 아무 역할도 안함
이건 명령어도 아니고 그냥 의미 없는 짓임
그니까 var StudentName; 이 항상 var StudentName = undefined; 가 아니라는것을 알아뒀으면 해!!!
진짜로 var StudentName= undefined 로 한다면 undefined 가 나와 그 이후엔.. 근데
칸이 부족해서 아무튼 예제 생략함
하튼 동일한 이름이 있는 반복된 선언은 아무것도 하지 않음
아 계속 똑같은 얘기만 한 10번은 하는듯
그럼 let 이나 const 는 어떨까??????
let studentName = "Frank";
console.log(studentName);
let studentName = "Suzy";
응 일단 실행이 안될거야... 글고 신택스 에러를 뿜어 내겠찌
let같은 경우엔 명시적으로 재 선언이 안됨
이건 그러라고 만든거야
글고 이건 소셜엔지니어링의 문제기도 함
재선언된 만흥ㄴ 변수들이 프로그램의 버그를 일으키곤함
그래서 es6 에서 let을 만든거시여!!!
- Constants?
const 키워드는 let 보다 제약이 많어
,let 처럼 같은 스코프에서 같은 이름으로 식별자가 반복될 수 없구
재 할당도 안됨 ㅇㅇ 그게 콘스트지
재할당이 안되기 때문에 const 는 언제나 선언시 할당이 필요함
글면 왜 재선언이 안되는지 알수 있찌
재선언엔 재할당이 포함됨 암튼 그래서 안됌 ㅇㅇ
const 의 재선언이 안되기 때문에 , 물론 TC39는 일관성을 유지 하기 위해 재선언은 하지말아야한다는 입장이지만,
이게 최선인지에 대해선 논란이 있어
그래도 적어도 이 결정엔 이유가 있는거지
Loops
ㅇㅇ 이전 이야기들에서 JS 는 우리가 동일스코프에선 재선언을 안했으면 좋겠따는걸 알수있지var keepGoing = true; while (keepGoing) { let value = Math.random(); if (value > 0.5) { keepGoing = false; } }
너무 직설적인 admonition 같다구?? 친구야 이코드는 어떻게 생각하니
value 는 반복문에서 재선언될까?? 에러가 반환될까 ?? ㄴㄴㄴㄴㄴ
요 요 요 let 선언의 재선언 금지 원칙은 언제나 스코프 인스턴스마다 적용되는 이야기야!!!
즉 새로운 스코프 갈때마다 모든게 새로워진다는 거지!!!
var 로 바꾸면 어캐될까??
var keepGoing = true;
while (keepGoing) {
var value = Math.random();
if (value > 0.5) {
keepGoing = false;
}
}
var 는 일단 블록스코프가 아니기 떄문에 전역변수에 붙어버려!!!
여기서도 물론 재선언은 일어나지 않아.. value 는 글로벌 스코프에 있으니까
너가 머릿속으로 선언 키워드들을 지워버리고 코드를 본다면 재선언이 일어날때 조금이라도 도움이 될꺼야
근데 for문에서는 과연 어떨까??
for (let i = 0; i < 3; i++) {
let value = i * 10;
console.log(`${ i }: ${ value }`);
}
// 0: 0
// 1: 10
// 2: 20
value 가 스코프가 만들어질때마다 만들어진다는건 확실하지 근데 i 는 어떨까?
재선언 될까?
그거에 답하기 위해서 i 가 어떤 스코프에 있는지 를 확인해보면됨
이게 밖에서 접근할수 있는것 같지만 아니야
이건 for loop 바디에 있는 스코프에있어 value랑 같은 스코프말이야
사실
{
// a fictional variable for illustration
let $$i = 0;
for ( /* nothing */; $$i < 3; $$i++) {
// here's our actual loop `i`!
let i = $$i;
let value = i * 10;
console.log(`${ i }: ${ value }`);
}
// 0: 0
// 1: 10
// 2: 20
}
일케 생각하면 편해
for .. of 문에선 어떨까??
ㅇㅇ for..in 문이랑 for..of 문에서도 동일한 원칙이 적용 됨
= 재선언 없다고!!!!!!!!!!!!!!
value 선언자가 let 대신 const 면 어떨까?
물론 재선언이 아니기 댐에 똑같은 결과임 ㅇㅇㅇㅇㅇㅇ
for in 과 for of 문에서는 아무 문제 없음
근데 일반적인 for 문에서 는 문제가 될수있음
for (const i = 0 ; i < 3 ; i++)
{
//TODO
};
ㅇㅇ 딱봐도 i++ 이 문제되게 보이지 이거 재할당임
이 간단한걸 너무 장황하게 설명하고 있으니 ㅉㅉㅉ
그냥 const 는 재할당이 필요한 보통의 for문에선 쓰지마 ㅅㄱ
물론 재할당만 안한다면 ㄱㅊㄱㅊ
Uninitialized Variables (aka, TDZ)
var 같은 경우엔 호이스팅된담에 undefined 로 초기화가 됨 그래서 우리는 전체 스코프에서 var 을 사용할 수잇음
근데 let ㅇ느 그렇지 않음
console.log(studentName);
let studentName = "Suzy";
위의 코드의 경우 레퍼런스 에러가 남
studentName 이 있끼는 하지만 초기화가 안된거란걸 암시하는것 같음
그러면 다음은 어떨까?
studentName = "Suzy"; //레퍼런스 에러
console.log(studentName);
let studentName;
어.. 초기화가 안됨
이건 초기화가 안됬다는데 왜그럴까???
진짜 문제는 어떻게 우리가 초기화되지 않은 변수들을 초기화 하느냐에 대한 문제들인데
let 이나 const 에게 있어서 초기화를 할 유일한 방법은 declaration statement(let/ const) 붙은채로 초기화를 해야한다는것!!
너무 진부한 예쩨는 절루 뺴고
let studentName;
// or:
// let studentName = undefined;
// ..
studentName = "Suzy";
console.log(studentName);
// Suzy
ㅇㅇ 일케 된데
Finaly Initialized
TBD