변수는
let이나var키워드를 사용해 선언하며, 값을 변경할 수 있는 저장공간입니다.
반면 상수는const키워드를 사용해 한 번 값을 할당하면 변경할 수 없습니다.데이터 타입은 크게 원시 타입과 참조 타입으로 나뉩니다.
원시 타입에는number,string,boolean,null,undefined,symbol,bigint등이 있고,
참조 타입에는 객체, 배열, 함수가 포함됩니다.연산자는 값이나 변수를 연산할 때 사용되며, 산술, 비교, 논리 연산자 등이 있습니다.
특히 엄격한 비교 연산자는 값과 타입을 모두 비교한다는 점에서 자주 사용됩니다.마지막으로 함수는 특정 작업을 수행하는 코드의 집합으로, 재사용성과 코드의 가독성을 높여줍니다.
'function' 키워드를 사용해서 선언식으로 작성할 수 있고, 화살표 형태의 표현식 형태로도 작성할 수 있습니다.
변수 선언에서는
let,const,var키워드를 사용합니다.
let과const는 블록 스코프를 가지며,var는 함수 스코프를 갖습니다.
또한const는 선언 시 반드시 초기화해야 하고, 재할당이 불가능한 상수입니다.데이터 타입은 크게 원시 타입과 참조 타입으로 나눌 수 있습니다.
원시 타입에는number,string,boolean,null,undefined가 있고,
참조 타입에는 객체, 배열, 함수가 있습니다.연산자에는 산술, 논리, 비교, 삼항 연산자 등이 있습니다.
특히 엄격한 비교 연산자는 값뿐만 아니라 데이터 타입까지 엄격하게 비교합니다.함수는 특정 작업을 수행하는 코드의 묶음으로,
함수 선언식, 함수 표현식, 그리고 화살표 함수 방식으로 정의할 수 있습니다.
이러한 함수들은 코드의 재사용성과 가독성을 높이는 데 중요한 역할을 합니다.
var, let, const 키워드로 변수를 선언할 수 있음var, let, const 차이점 요약
| 키워드 | 스코프(scope) | 재할당 | 재선언 | 특징 |
|---|---|---|---|---|
var | 함수 스코프 | 가능 | 가능 | 호이스팅 시 undefined로 초기화됨 |
let | 블록 스코프 | 가능 | 불가능 | 중복 선언 불가, TDZ(Temporal Dead Zone) 존재 |
const | 블록 스코프 | 불가능 | 불가능 | 선언과 동시에 초기화 필요 |
예시
var a = 1;
let b = 2;
const c = 3;
b = 5; // 가능
// c = 10; // 불가능, 오류 발생 (재할당 불가)
let의 핵심 특징let 변수를 다시 선언할 수 없음var의 중복 선언으로 인한 실수를 줄이고, 의도치 않은 변수 덮어쓰기를 방지하기 위함{
let x = 1;
// let x = 2; // 재선언(중복 선언) 불가능, 오류 발생
x = 2; // 재할당은 가능
} let a = 1;
{
let a = 2; // 바깥 a를 가리는('섀도잉') 새 변수
// 여기 블록 안에서는 a === 2
}
// 블록 밖에서는 a === 1function f(p) {
// let p = 10; // 불가능, 오류 발생
}let/const 선언이 호이스팅은 되지만 초기화되기 전까지의 구간에서 그 식별자에 접근하면 ReferenceError가 나는 현상var의 암묵적 undefined 초기화로 생기던 버그를 방지함let/const도 호이스팅됨 → 다만 초기화되지 않은 상태(uninitialized)로 올라가며, 초기화 시점(선언문 도달) 전 접근이 금지됨typeof도 ReferenceError를 던짐console.log(x); // ❌ ReferenceError (TDZ)
let x = 1;console.log(typeof y); // ❌ ReferenceError (TDZ)
let y = 3;➡️ var로 선언했다면 typeof y → "undefined"가 되었지만, let은 TDZ 때문에 에러가 남// 매개변수 기본값은 좌→우 순서로 평가됨
function g(a = b, b = 2) { // a 평가 시점에 b는 아직 TDZ
return a + b;
}
// g(); // ❌ ReferenceError: b is not defined (TDZ)// 각 반복마다 i의 새 바인딩이 생김 (클로저 안전)
const fns = [];
for (let i = 0; i < 3; i++) {
fns.push(() => i); // 배열 fns에 i를 반환하는 함수를 저장
}
fns[0](); // 0
fns[1](); // 1
fns[2](); // 2
- per-iteration binding: 반복마다 새로운 변수 바인딩이 생성되는 동작
- 각 함수는 자신이 만들어질 당시의 i를 기억함
- 그래서 나중에 호출할 때마다, 당시의 i 값을 돌려줌
let의 반복별 바인딩 덕분에, 전통적인var+ 클로저 문제를 피함
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(() => i);
}
fns[0](); // 3
fns[1](); // 3
fns[2](); // 3
var는 루프마다 새 변수를 만들지 않고, 하나의 i만 공유함- 따라서 함수들이 같은 i를 바라보다가, 반복이 끝나면 i=3이 되므로 전부 3을 반환함
{
// TDZ 시작
// console.log(z); // ❌ ReferenceError
let z = 10; // 여기서 초기화되며 TDZ 종료
console.log(z); // 10
}개념: const 키워드로 선언하며, 한 번 할당한 값은 변경할 수 없음
const MAX_COUNT = 10;
MAX_COUNT = 20; // 오류 발생
주의: const는 값 자체를 재할당할 수 없다는 뜻이지, 객체 내부의 속성은 여전히 변경 가능함
const user = { name: "Kim" };
user.name = "Lee"; // 가능
분류
| 구분 | 타입 | 설명 |
|---|---|---|
| 원시 타입 (Primitive) | number, string, boolean, null, undefined, symbol, bigint | 값 자체를 저장 |
| 참조 타입 (Reference) | object, array, function 등 | 메모리의 참조(주소)를 저장 |
예시
let num = 42; // number
let str = "Hello"; // string
let isTrue = false; // boolean
let obj = { name: "JS" }; // object
let arr = [1, 2, 3]; // array
let func = function() {}; // function
null은 "값이 없음", undefined는 "정의되지 않음"typeof null → "object" (자바스크립트 설계 오류)원시 타입 vs. 참조 타입
원시 타입은 불변
🔍 "불변(immutable)"의 의미
- 한 번 만들어진 값은 절대 변경되지 않음
- 변하는 것처럼 보이지만, 사실은 새로운 값이 만들어지고 기존 값은 버려지는 것임
✔️let a = 'hello'; a = a + 'world'; console.log(a); // "hello world"'hello'→'hello world'로 바뀐 것처럼 보이지만, 실제로는'hello'문자열이 바뀐 게 아니라 새로운 문자열'hello world'가 새 메모리 공간에 생성된 것임
✔️ 원래'hello'는 그대로 남고, 변수a가 새 주소를 가리키게 된 것임
🔍 불변의 의미를 숫자 예시로
let x = 10; let y = x; y = 20; console.log(x); // 10✔️
x와y는 각각 값을 복사해서 저장함
✔️y를 바꿔도x에는 영향이 없음 → 원시 타입은 "값 복사(pass by value)"이기 때문
참조 타입은 가변
🔍 "가변(mutable)"의 의미
- 값이 저장된 객체의 내부 상태를 바꿀 수 있다는 뜻임
- 즉, 새로 만들지 않아도 기존 객체의 속성을 변경할 수 있음
✔️const user = { name: "Kim", age: 20 }; user.age = 21; // 내부 속성 변경 console.log(user); // { name: "Kim", age: 21 }user라는 객체 자체는 그대로고, 내부의age프로퍼티만 바뀐 것임
✔️ 즉, 같은 객체 주소를 가리키면서 내부 값만 수정된 것임
🔍 복사 시의 차이
const a = { value: 1 }; const b = a; b.value = 2; console.log(a.value); // 2✔️
a와b는 같은 객체 주소를 공유함
✔️ 따라서 하나를 수정하면, 다른 것도 영향을 받음
✔️ 참조 타입은 "참조(주소) 복사(pass by reference)" 방식으로 동작하기 때문임
주요 연산자 종류
| 분류 | 예시 | 설명 |
|---|---|---|
| 산술 연산자 | +, -, *, /, % | 사칙연산 |
| 대입 연산자 | =, +=, -= | 값 할당 |
| 비교 연산자 | ==, ===, !=, !==, >, < | 값 또는 타입 비교 |
| 논리 연산자 | &&, ||, ! | 논리 연산 및 조건 평가 |
| 삼항 연산자 | 조건 ? 참 : 거짓 | 한 줄 조건문 |
const a = 5, b = "5";
console.log(a == b); // true (값만 비교)
console.log(a === b); // false (값과 타입 모두 비교)개념: 특정 작업을 수행하는 코드의 묶음으로, 코드를 재사용하고 모듈화할 수 있게 해줌
선언 방식
| 종류 | 예시 | 특징 |
|---|---|---|
| 함수 선언식 | function add(a, b) { return a + b; } | 호이스팅 가능 |
| 함수 표현식 | const add = function(a, b) { ... } | 변수에 함수 저장, 호이스팅 X |
| 화살표 함수 | const add = (a, b) => a + b; | this 바인딩 없음, 간결함 |
// 선언식
function greet(name) {
return `Hello, ${name}`;
}
// 화살표 함수
const greet2 = (name) => `Hi, ${name}`;function add(a, b) {
return a + b;
}특징
| 항목 | 설명 |
|---|---|
| 호이스팅(Hoisting) | 가능 ➡️ 선언 전에 호출해도 동작함 |
| 저장 위치 | 스코프(전역 또는 함수 스코프)에 함수 이름으로 저장됨 |
| this | 호출 방식에 따라 결정됨 (일반 함수의 this 규칙 적용) |
| 선언 시점 | 코드가 실행되기 전에 메모리에 미리 등록됨 |
| 장점 | 가독성이 좋고, 함수 호출을 코드 위쪽에서도 가능 |
| 단점 | 변수명 충돌 위험 (특히 전역 스코프에서) |
예시
console.log(sum(2, 3)); // ✅ 동작 (호이스팅됨)
function sum(a, b) {
return a + b;
}
const add = function(a, b) {
return a + b;
};특징
| 항목 | 설명 |
|---|---|
| 호이스팅(Hoisting) | 불가능 ➡️ 변수 선언만 호이스팅, 초기화는 나중에 됨 |
| 저장 위치 | 변수(const, let, var)에 저장됨 |
| this | 호출 컨텍스트에 따라 결정됨 (일반 함수의 this 규칙 적용) |
| 선언 시점 | 실행 흐름이 해당 줄에 도달할 때 함수가 생성됨 |
| 장점 | 익명 함수 가능, 스코프 안전하게 관리 가능 |
| 단점 | 선언 전에 호출 불가능 |
예시
console.log(add(2, 3)); // ❌ ReferenceError
const add = function(a, b) {
return a + b;
};
const add = (a, b) => a + b;특징
| 항목 | 설명 |
|---|---|
| 호이스팅(Hoisting) | 불가능 ➡️ 변수 선언만 호이스팅, 초기화는 나중에 됨 |
| this | 없음 ➡️ 상위 스코프의 this를 그대로 사용 (lexical this) |
| arguments 객체 | 없음 ➡️ 명시적으로 전달해야 함 |
| return 문법 | 중괄호({}) 생략 시 한 줄이면 자동 return |
| 사용 목적 | 콜백, 짧은 함수, 리액트 이벤트 핸들러 등 |
| 주의점 | 생성자(new)로 사용 불가, 메서드 정의에 부적합 |
⭐
arguments객체🔍 핵심
- 일반 함수(선언식/표현식)는 호출 시 전달된 인수를 암묵적 객체
arguments로 제공함- 화살표 함수는 자신만의
arguments가 없음- 그래서 화살표 함수 안에서
arguments를 쓰면 스코프 체인(바깥 함수)의 것을 참조하거나, 아예 ReferenceError가 남- 따라서 화살표 함수에서 인수들을 쓰려면 매개변수로 명시적으로 받거나,
...rest(rest 파라미터)를 사용해야 함🔍 비교 예시
- 일반 함수는
arguments가 있다function sum() { // 유사 배열 객체 return Array.from(arguments).reduce((a, b) => a + b, 0); } sum(1, 2, 3); // 6- 화살표 함수는
arguments가 없다const sum = () => { console.log(arguments); // ❌ 화살표 함수 자체엔 없음 (ReferenceError 또는 바깥 스코프 것) };- 화살표 함수에서는
...rest로 직접 받기const sum = (...nums) => nums.reduce((a, b) => a + b, 0); sum(1, 2, 3); // 6- 바깥(일반) 함수의
arguments를 상속해서 쓰는 패턴function wrapper() { const inner = () => { // 여기서의 arguments는 wrapper의 arguments를 가리킴 return Array.from(arguments).join("-"); }; return inner(); } wrapper("a", "b"); // "a-b"
- 화살표 함수는 자체
arguments가 없고, 렉시컬하게 바깥의 것을 캡처할 수는 있음- 그러나 이 동작에 의존하면 가독성이 떨어지니,
...rest형태로 쓰는 것을 권장함
const obj = {
value: 10,
normal() {
console.log(this.value); // 10
},
arrow: () => {
console.log(this.value); // ❌ undefined (상위 스코프의 this 사용)
},
};
obj.normal(); // 10
obj.arrow(); // undefined🔍
obj.normal(): 일반 함수
normal은 일반 함수(메서드)이기 때문에 호출 주체(obj)가this가 됨- 즉,
this는obj를 가리킴- 그래서
this.value는obj.value인 10이 출력됨
🔍
obj.arrow(): 화살표 함수
arrow는 화살표 함수(arrow function)이기 때문에 자신만의this를 가지지 않음- 대신, 화살표 함수가 선언된 스코프의
this를 그대로 사용함 (이를 렉시컬 this라고 함)- 이 코드에서
arrow는 전역 스코프에서 정의된 객체 리터럴 내부에 있으므로, 전역 스코프의this를 그대로 물려받음
✅ 브라우저 환경에서는 전역 스코프의this→window
✅ Node.js 환경에서는 전역 스코프의this→{}(모듈 객체)- 즉,
this는obj가 아닌 전역 객체이므로,this.value는 존재하지 않아undefined가 나옴