조원과 함께 스코프, 호이스팅, 실행 컨텍스트 등에 대한 조사를 한 후 공유를 하는 시간을 가졌다.
scope는 우리말로 번역하면 범위. 즉 스코프란 변수에 접근할 수 있는 범위
JS에서는 전역 스코프와 지역 스코프
대부분의 프로그래밍 언어는 블록 레벨 스코프를 따르지만 JS는 함수 레벨 스코프를 따른다.
함수 내 선언된 변수는 함수 내에서만 유효하며 외부에서는 참조할 수 없다. 즉, 함수 내부에서 선언한 변수는 지역 변수,
함수 외부에서 선언한 변수는 지역 변수, 함수 외부에서 선언한 변수는 모두 전역 변수이다.
모든 코드 블록{} (함수, if문, for문, while문, try/catch문 등) 내에서 선언한 변수는 해당 코드 블럭 내에서만 유효하며,
해당 코드 블록{} 외부에서는 참조할 수 없습니다. 즉, 코드 블록 내부에서 선언한 변수는 지역 변수이다.
자바스크립트에서 스코프를 구분해보면 다음과 같이 2가지로 나눌 수 있다.
코드 어디에서든지 참조할 수 있다.
함수 코드 블록이 만든 스코프로 함수 자신과 하위 함수에서만 참조할 수 있다.
모든 변수는 스코프를 갖는다. 변수의 관점에서 스코프를 구분하면 다음과 같이 2가지로 나눌 수 있다.
전역에서 선언된 변수이며 어디에든 참조할 수 있다.
지역(함수)내에서 선언된 변수이며 그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.
변수는 선언 위치(전역 또는 지역)에 의해 스코프를 가지게 된다. 즉, 전역에서 선언된 변수는 전역 스코프를 갖는 전역 변수이고,
지역(자바스크립트의 경우 함수 내부)에서 선언된 변수는 지역 스코프를 갖는 지역 변수가 된다.
전역 스코프를 갖는 전역 변수는 전역(코드 어디서든지)에서 참조할 수 있다. 지역(함수 내부)에서 선언된 지역 변수는
그 지역과 그 지역의 하부 지역에서만 참조할 수 있다.
JS는 ES6에서 도입된 let, const를 포함해 모든 선언 (var, let, const, function, class)을 호이스팅한다.
여기서 호이스팅(hoisting)이란, var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성을 말한다.
하지만 var키워드로 선언된 변수와 달리 let 키워드로 선언된 변수를 선언문 이전에 참조하면 참조 에러 (ReferenceError)가 발생한다.
이는 let키워드로 선언된 변수는 스코프의 시작에서 변수 선언까지 일시적 사각지대 (Temporal Dead Zone (TDG))에 빠지기 때문이다.
변수 선언은 소스코드가 한 줄씩 순차적으로 실행되는 시점, 즉 런타임이 아닌, 그 이전 단계에서 먼저 실행된다. 자바스크립트 엔진은
먼저 한 줄씩 순차적으로 실행하기에 앞서 평가 과정을 거치고 소스코드의 실행을 위한 준비를 한다. 이 때 자바스크립트 엔진은
변수 선언을 포함한 모든 선언문을 코드에서 찾아 먼저 실행한다. 그리고 평가 과정이 끝나면 변수 선언을 포함한 모든 선언문을 제외하고
소스코드를 한 줄씩 순차적으로 실행한다. 즉, 자바스크립트 엔진은 변수 선언이 어디에 위치해 있든 먼저 실행하게 된다.
이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅이라고 한다.
또한 변수의 경우 호이스팅이 진행되었다고 해도 실질적인 값의 할당은 런타임 시점에 undefined가 저장된 공간이 아닌 새로운
메모리 공간에 값 할당이 이루어진다. undefined가 저장된 공간은 더 이상 사용되지 않기 때문에 가비지 콜렉터에 의해 언젠가 제거된다.
JS에서 호이스팅이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.
var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화한다.
반면 let과 const로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않는다.
호이스팅을 설명할 땐 주로 "변수의 선언과 초기화를 분리한 후, 선언만 코드의 최상단으로 옮기는" 것으로 말하곤 한다.
따라서 변수를 정의하는 코드보다 사용하는 코드가 앞서 등장할 수 있다.
다만 선언과 초기화를 함께 수행하는 경우, 선언 코드까지 실행해야 변수가 초기화된 상태가 됨을 주의해야 한다.
-정적 언어로는 C, C#, C++, Java 등의 언어가 있다.
컴파일 시에 타입에 대한 정보를 결정하기 때문에 속도가 빠르고, 타입 에러로 인한 문제점을 초기에 발견할 수 있어 타입의 안정성이 올라간다.
코드의 가독성이 좋아 다수의 협업이나 프로젝트를 할 때 유지보수에 유리하다.
하지만 코드를 작성할 때 매번 타입을 결정해주어야 한다.
-동적 언어로는 JavaScript, Ruby, Python 등이 있다.
코드 실행시 자동적으로 변수의 타입을 판단하여 지정해준다. 또한 타입을 매번 지정할 필요가 없으므로 작성자가 보다 빠르게 코드를 작성할 수 있다.
하지만 실행 도중에 변수에 예상치 못한 자료형이 들어와 타입 에러를 발생시킬 수 있고, 타입 관련 에러는 런타임 시 확인 가능하기 때문에
코드가 길고 복잡해질 경우엔 타입 에러를 찾기가 어려워진다.
-변수는 값을 저장하기 위해 명명된 위치이다. 이것을 사용하면 미리 정해진 이름을 통해 예측할 수 없는 값에 접근할 수 있다.
-식별자는 코드 내의 변수, 함수, 함수 혹은 속성을 식별하는 문자열이다. JavaScript의 식별자는 대소문자를 구별하며 유니코드 글자, 숫자로
구성할 수 있지만, 숫자로 시작할 수는 없다.
기본형에는 값을 그대로 할당하고, 참조형에는 값이 저장된 주소값을 할당한다는 차이가 있다.
-문자열 값은 한 번 만든 값을 바꿀 수 없고, 숫자 값도 다른 값으로 변경할 수 없다. 변경을 새로 만드는 동작을 통해서만 이뤄진다.
한 번 만들어진 값은 가비지 컬렉팅을 당하지 않는 한 영원히 변하지 않는다.
얕은 복사는 객체를 복사할 때 위의 예제처럼 원래값과 복사된 값이 같은 참조를 가리키고,
깊은 복사는 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 값을 만든다.
변수가 어디부터 어디까지 참조되는지 이해할 수 없으므로, 전체적인 진행을 이해할 수 없을 뿐 더러, 이후에 수정도 쉽지 않을 것이다.
아직 답변을 하지 못한 문제에 대해선 조금 더 고민해봐야겠다...