자바스크립트 오해풀기— SCOPE
그 동안 자바스크립트 공부를 하면서 명확하게 잡히지 않았던 것들만 골라 경험담과 함께 시리즈로 정리하려한다. 카페트 밑의 먼지 마냥 급하진 않았지만 거슬리는 기본적 개념, 근본적인 동작에 대한 갈망을 이번 시리즈로 해결하고자 한다.
SCOPE
let, const 같은 키워드는 ES의 패러다임 바꾼다. 좋다고 하니까, 일단 let을 쓰라는 가이드만 따르다보니 let을 쓰지 않음으로서 생기는 오류를 접할 기회가 없었다. 결국 [지역, 전역] 그리고 [var, let, const]의 차이와 구체적인 동작을 명확히 모른채 약간 혼란스러운 상태에 있었다. JSLint 를 쓰면 문제를 바로 지적해주니 당장 문제는 회피할 수 있지만 도대체 무슨일이 벌어지는지, 어디에서 어디로 프레임이 옮겨 갔는지 알아야 한다고 생각한다.
ISSUE (1) 지역변수와 전역변수
let을 만나고 내가 헛갈린건 대다수 언어가 ‘블록단위 스코프’를 쓰기 때문이다. 자바는 전역을 쓰고 싶다면 그냥 static 을 붙인다. 그덕에 ‘JS에서는 var, const가 static 인가?’라고 착각을 했다. 아니다. 지역과 전역은 let 같은 선언 키워드의 문제가 아니다.
ISSUE (2) 함수 스코프의 부연설명
이 자바스크립트의 맹점을 기계적으로 회피하는게 아니라 실제로 이해하려면 결국 글로벌 객체와 실행문맥을 선언 키워드와 연결해서 이해해야한다.
— 자바스크립트 함수 스코프가 기본이다.
— 명시되지는 않았지만 작성된 코드는 브라우저의 window객체를 기준으로 실행된다.
— 즉 브라우저측에서 우리가 다루는 전역변수의 실체는 window 객체의 프로퍼티다. (엄밀히 따지면 같은 전역변수라도 var와 let은 다르다. let은 window프로퍼티가 아니다. 아래에서 정리한다.)
— 첫 예제의 test 외부에서 선언된건 모두 전역변수다. 다른 실행컨텍스트 즉 다른 함수에서 전역변수를 선언하는 방법은 (3)에서 설명
—따라서 예제의 test 함수 안에 선언된것들은 무슨 키워드를 쓰든 지역변수다. 즉 키워드가 아니라 어떤 함수스코프에서 선언이 되었느냐가 변수의 호출범위를 좌지우지한다.
— 그렇게 의미가 없다면 선언 키워드 간 차이는?
함수 스코프를 따르고 재선언 및 변수변경이 가능한 < var >
블록 스코프를 사용하고 재선언이 불가능한 공통점을 가진 < let >, < const >
글로벌객체 (또는 실행함수)에 프로퍼티를 형성하는 < var >
글로벌객체 (또는 실행함수)에 프로퍼티를 형성하지 않는 < let >, < const >
< let >과 < const >의 차이점은 값 변경가능여부다.
ISSUE (3) 어떻게 전역을 선언하나?
첫 예제의 ‘noVar’ 처럼 그냥 키워드 없이 쓰는게 JAVA의 static 에 해당한다는걸 알수 있다. 특별히 정하지 않거나 var를 사용하면 변수가 죄다 전역객체에 붙어버린다. 혼자만 쓰는것도 아닌데 글로벌 객체에 덕지덕지 붙는건 문제를 일으킬것 같다. let으로 블럭 스코프를 기준으로 변수를 관리하는 것이 의미가 있다는걸 알 수 있다.
ISSUE (4) 함수의 내용은 처음부터 실행 되지는 않는다.
let, var, function, class, {} 같은 선언(statement)은 코드가 읽힐때 호이스팅된다. 이때 호이스팅은 선언과 실행의 순서가 거꾸로라도 Reference 에러가 나지 않는 차이가 있는거지 내용물을 실행하는건 아니다. 리터럴 형식으로 정의된 함수도 마찬가지다. var 에 undefine이 먼저 정의할뿐이지 할당은 순차적인 작업에서 시행된다.
— 그렇기 때문에 예제에서 test() 하기 전 noVar의 호출은 에러를 일으키지만 호출후에는 전역변수가 되어서 스코프에 잡혀 호출에도 에러가 발생하지 않는다.
— 하지만 바로 실행되지 않는다고해서 실시간으로 호출되는 형태에 따라 Scope가 정해진다고 생각하면 안된다. 스코프는 함수가 사용할 준비가 될때 (호이스팅 될때 정해진다.)
스코프는 바뀌지 않는다. 어떻게 호출하든 함수의 위치만이 영향을 미친다는 거다. 처음 정해진것을 따라간다는 이야기다.