1. 변수 구분
변수는 로컬(지역) 변수와 글로벌(전역) 변수 2가지 종류가 있다.
이렇게 종류가 나눠진 이유는 각 변수의 기능과 목적이 다르기 때문이다.
로컬 변수
함수 내에서 선언된 변수로 해당 함수의 스코프에서만 유효하다. 그래서 함수가 실행되면 생성되고 실행이 끝나면 소멸한다.
로컬 변수는 함수 내에서만 접근이 가능하며 외부에서는 이 변수에 대해 접근할 수 없다.
그래서 서로 다른 함수에서는 동일한 이름의 로컬 변수를 사용할 수 있다. (각 함수의 스코프가 다르기 때문에 충돌이 일어나지 않는다.)
글로벌 변수
전역 스코프에서 선언되고 사용되는 변수로 어디에서든 접근 가능한 가장 바깥 쪽의 스코프로, 함수 밖에서 선언된 것들을 의미한다.글로벌 변수는 다른 JS 파일에서 변수 값을 공유할 수 있고, 파일에서는 공통적인 변수의 개념으로 사용된다.다만 많이 사용할 경우 변수의 충돌 가능성이 높아지고, 가독성이 낮아지거나 유지보수를 하기 어려워 좁게 유지하는 것을 권장한다.
value = 100이 글로벌 변수인데, 이후 point라는 함수 안에서 300으로 값이 변경된다.
이런 식으로 함수 안에 글로벌 변수를 변경하는 것은 권장되지 않는다.
왜냐하면 이미 글로벌 변수로 맨 위에 선언한 값을 수정하면 위에 말한 것과 동일하게 가독성이 낮고 유지보수가 어려워지기 때문이다.
스크립트의 최상단에 'use Strict'를 선언하면 엄격 모드가 활성화된다.
이걸 사용하면 전역 변수가 암묵적으로 생성되는 것을 막는다.
이런 식으로 value가 어떤 선언 없이 전역 변수가 되는 것을 막는다.
이대로 실행하면 출력 값은 "글로벌 변수 사용 불가"가 나온다.
2. 스코프 유형
먼저 스코프가 뭔지 알아보자.
스코프는 변수의 유효 범위를 나타낸다.
변수가 어디에서 선언되었는지에 따라 해당 변수에 접근할 수 있는 범위가 결정된다.
1. 전역 스코프
코드의 가장 바깥 쪽에 정의되는 스코프로 전역 스코프에 선언된 변수는 어디에서든지 접근할 수 있다.
var, let, const 모두 전역 스코프에서 변수를 선언할 수 있지만, var만 전역 객체의 프로퍼티로 등록된다.
(전역 객체란 웹 브라우저에서는 window를, Node.js에서는 global객체를 의미한다.
전역 객체는 프로그램이 시작될 때 자동으로 생성되며 코드 어디든 접근할 수 있다.
웹 개발 시 setTimeout, Math 같은 내장 함수나 객체에 접근할 수 있는 것)
2. 함수 스코프
함수 안에서만 유효한 스코프로, 함수 안에서 선언된 변수는 함수 외부에서 접근할 수 없다.
함수 스코프 안에 있을 때만 유효하기 때문에, 다른 함수에 동일한 이름의 변수가 있어도 영향을 미치지 않는다.
3. 블록 스코프
중괄호 {}에 둘러 싸인 코드 안에서만 변수가 유효한 스코프다.
예를 들어 if, for 문 같이 특정한 ~문 안에서 선언된 변수는 그 블록 안에서만 유효하다.
ES6에서 도입된 let, const를 사용하면 이런 블록 스코프를 가진 변수를 선언할 수 있다.
(var는 블록 스코프를 갖지 않는다.)
try-catch문과 switch-case문도 블록 스코프로 아래와 같은 결과를 가져온다.
let sports = "축구";
try {
let sports = "농구";
console.log("안: ", sports);
abc = error;
} catch(e) {
console.log("catch: ", sports);
};
console.log("밖: ", sports);
//안: 농구
//밖: 축구
let item = 1;
switch (item) {
case 1:
let sports;
break;
case 2:
// let sports;
default:
console.log(sports)
};
//undefined
3. let 변수와 블록 스코프를 자세히 알아보자
위에서 말했던 것처럼 let 변수는 블록 스코프를 가진다.
해당 블록에서만 적용되기 때문에 다른 블록 내에 동일한 이름의 변수가 있어도 각각을 독립적인 변수로 취급한다.
function example() {
let x = 10; // 첫 번째 블록 스코프 변수 x 선언
if (true) {
let x = 20; // 두 번째 블록 스코프 변수 x 선언
console.log(x); // 출력: 20 (현재 블록 내의 x)
}
console.log(x); // 출력: 10 (첫 번째 블록 내의 x)
}
example();
맨 처음 선언한 let x = 10과 if문 안에 있는 let x = 20은 같은 이름이지만 값이 다르고, 선언된 위치가 달라 결과도 다르게 나온다.
4. let과 var를 비교해보자
var k 로 선언할 경우 어떤 걸 클릭하든 이미 반복문이 끝난 후의 k = 3을 출력한다.
let의 경우 블록 스코프라서 클릭하는 부분들이 k로 출력된다.
5. let 변수와 this
먼저 this가 무엇일까?
this는 JS에서 실행 중인 코드의 실행 문맥에서 참조하는 객체를 가리키는 키워드다.
무슨 말인지 모르겠다고? 나도 그렇다. 아래 예시 코드를 봐보자.
this는 함수 내부나 메서드 안에서 사용될 때 그 함수, 그 메서드를 호출한 객체를 가리킨다.
console.log(this); // 전역 객체 (브라우저에서는 window, Node.js에서는 global)
function greet() {
console.log(this); // 호출 시점에 따라 달라짐
}
greet(); // 전역 객체 (브라우저에서는 window, Node.js에서는 global)
const obj = {
name: "Alice",
greet() {
console.log(this.name);
}
};
obj.greet(); // "Alice"
여기서부터 좀 복잡한데, 위에서 말한 것처럼 var, let, const 모두 전역 변수로 선언은 할 수 있다.
다만 var만이 전역 객체의 프로퍼티가 된다. let, const는 블록 스코프를 가져야 하는데 그러지 못하므로 저장 영역이 달라진다.
그래서 자바 스크립트 엔진이 블록을 만들고, 이를 스코프로 사용하는 개념이다.
6. 호이스팅
먼저 JS에서 실행 콘텍스트 처리 순서는 아래와 같다.
1. 전역 실행 콘텍스트 생성
- 스크립트가 실행되면 가장 먼저 전역 실행 컨텍스트가 생성된다. (전역 환경에서 코드를 실행하는데 필요한 초기 설정 담당)
- 전역 객체와 전역 스코프가 생성되며 전역 변수와 함수의 선언이 처리된다.
2. 실행 컨텍스트 생성
- 함수가 호출되면 해당 함수의 실행 콘텍스트가 생성된다. (함수가 실행될 때 필요한 환경 구성, 해당 함수의 변수 및 함수 선언 처리)
- 스택에 순서대로 쌓이며 가장 위에 있는 실행 콘텍스트가 현재 실행중인 콘텍스트가 된다.
3. 변수 및 함수 선언 처리 ⭐️
- 변수는 메모리에 할당되고 undefined로 초기화 된다.
(**JS에서 변수는 사용하기 전에 선언되어야 한다. 선언될 때 메모리에 해당 변수를 위한 공간을 확보한다.
변수에 실제 값을 해당하기 전까지, 변수는 정의되었지만 값이 없는 상태인 undefined가 된다.**)
- 함수는 메모리에 저장되고 실제 함수 객체로 초기화 된다.
- 이 과정에서 호이스팅이 발생하며 변수와 함수 선언문이 해당 스코프의 최상단으로 이동한다.
4. 코드 실행
- 변수 할당, 함수 호출, 조건문 실행, 반복문 실행 등 코드가 순차적으로 실행된다.
5. 콘텍스트 종료
- 코드 실행이 완료되면 해당 콘텍스트에서 생성된 변수와 함수를 메모리에서 제거하고 다음 콘텍스트로 돌아간다.
- 이전에 있던 실행 콘텍스트는 종료되어 스택에서 제거된다.
그래서 호이스팅이 뭐냐면...
JS에서 이 3번, 변수 및 함수 선언이 해당 스코프의 최상단으로 끌어올려지는 동작을 말한다.
여기서 var, let, const의 차이점이 드러난다.
변수 및 함수가 선언되고 할당되어야 하는데, 호이스팅은 변수의 선언만 최상단으로 이동 시킨다.
var는 이 선언과 할당이 따로따로 분리될 수 있어서 선언 후 초기화 되면 아직 할당만 하지 않은 상태가 된다.
그래서 넣을 곳은 있는데 아직 값이 들어오지 않았기에 undefined로 찍힌다. 바로 아래처럼!
하지만 let, const는 선언과 초기화가 따로 분리되지 않는다.
선언-(초기화-값 할당)의 프로세스에서 초기화 되기 전까지는 TDZ(Temporal Dead Zone)에 위치해 있다.
여기에 있는 동안에는 변수에 접근할 수 없어서 "어? 얘 아직 할당 못했는데?"하고 위와 같은 오류가 발생하는 것이다.
이후에 let, const에서도 차이점이 있다.
let은 선언 후 값을 할당하지 않으면 초기화 상태(undefined)로 뜬다.
const는 선언 후 값을 동시에 할당해주어야 한다. 그렇지 않으면 오류가 발생한다.
var | let | const | |
호이스팅 | 가능 | 가능 | 가능 |
선언과 초기화 분리됨 | 가능 | 불가능 | 불가능 |
선언과 값 할당 해야 함 | 안해도 됨 | 안해도 됨 | 해야 함 |
즉 셋 다 호이스팅 자체는 가능하나, 변수 선언 후 초기화 되는 과정에서 선언과 초기화가 분리되느냐로 나눠진다.
let, const는 선언과 초기화가 동시에 이뤄져야 하기 때문에 먼저 선언만 하는 것에서 오류가 발생하고
const는 선언과 초기화와 값 할당이 동시에 이뤄져야 하기 때문에 단순히 선언만 할 시에 오류가 발생하는 것이다.
스스로 납득이 안 돼서 다시 수정했다.
7. Const
값을 바꿀 수 없는 변수를 선언할 때 const를 쓴다.
다만 배열이나 객체에서 push, slice 같은 메서드를 사용하면 원본이 수정될 수 있으며,
마찬가지로 배열이나 객체의 부분은 수정할 수 있으나 전체를 통째로 수정할 수는 없다.
const는 선언과 동시에 할당하기 때문에 다시 선언도, 할당도 할 수 없다.
let은 선언만 할 수 없고 다시 할당은 할 수 있다. (값은 바꿀 수 있음)
var는 둘 다 할 수 있기 때문에 코드가 꼬이기 쉽다.
JS에는 var, let, const 3가지 종류의 선언 키워드가 있다.
var는 어디든 뛰어 다닐 수 있는 놈이고, 전역 객체의 프로퍼티가 될 수 있다.
선언과 할당이 동시에 이뤄지지 않아 호이스팅 시 오류가 나지 않는다.
이 놈은 과거에 쓰이던 방식인데, 어디든 뛰어 다니는 놈이라 위험해서 사용을 자제하는 것이 좋다.
let은 블록 스코프로 해당되는 블록 안에서만 영향을 미친다.
변수의 선언은 호이스팅이 이뤄지지만, 값을 부여하는 초기화는 변수 선언문에 도달했을 때만 이루어진다.
const도 블록 스코프로 해당되는 블록 안에서만 영향을 미친다.
let과 동일하게 선언은 호이스팅이 되지만 값을 부여하는 초기화는 선언문에 도달했을 때만 이뤄진다.
더불어 const는 선언과 동시에 할당되므로 재선언, 재할당이 되지 않는다.
간단하게 정리하면 이렇게 되려나......
단순 구현이 아니라 작동 원리를 이해하려고 하니까 어렵다. 🥲
'👋🏻 JavaScript > 📖 자바스크립트 ES6+' 카테고리의 다른 글
[JS] 객체 Map을 알아보자 (0) | 2023.06.09 |
---|---|
[JS] JavaScript의 실행 콘텍스트와 체인 스코프에 대해 알아보자 (0) | 2023.06.07 |
[JavaScript] Generator & Iterator (0) | 2023.05.12 |
[JavaScript] 정규표현식 알아보기 (0) | 2023.04.28 |
[JavaScript] filter() 로 특정 인덱스의 요소들만 반환해보기 (0) | 2023.04.25 |