언어/Javascript
1104 자바스크립트 기초 개념 정리 - 국제화, 프로토타입, 스코프, 클로저 등
thinktank911
2025. 11. 4. 18:26
예외 상황
🧩 예외 상황의 종류와 처리
자바스크립트에서는 코드 실행 중 다양한 에러(예외) 가 발생할 수 있습니다.
이러한 예외는 크게 두 가지로 나눌 수 있습니다.
💡 1️⃣ ECMAScript Error
자바스크립트 언어 자체에서 발생하는 기본적인 에러 타입
에러 종류발생 시점예시
| RangeError | 값이 허용 범위를 벗어날 때 | js<br>new Array(-1); // RangeError<br> |
| ReferenceError | 존재하지 않는 변수를 참조할 때 | js<br>console.log(x); // ReferenceError<br> |
| SyntaxError | 문법이 잘못되었을 때 | js<br>eval("if (true) {"); // SyntaxError<br> |
| TypeError | 자료형이 잘못되어 연산이 불가능할 때 | js<br>null.f(); // TypeError<br> |
🧠 정리
- RangeError → 범위를 벗어남
- ReferenceError → 선언되지 않은 변수 참조
- SyntaxError → 문법 오류
- TypeError → 타입 불일치
🌐 2️⃣ DOMException
Web API 사용 중 발생하는 브라우저 환경의 에러 타입
에러 종류발생 시점예시
| NetworkError | 네트워크 요청 실패 시 | fetch("wrong-url") |
| AbortError | 요청이 중단되었을 때 | controller.abort() 호출 시 |
| TimeoutError | 제한 시간이 초과되었을 때 | 일정 시간 초과 시 발생 |
🧠 정리
- NetworkError → 서버 연결 실패
- AbortError → 요청이 중단됨
- TimeoutError → 요청 시간 초과
⚙️ 3️⃣ try...catch문
코드 실행 중 발생할 수 있는 예외 상황을 처리하는 문법
try {
// 예외가 발생할 수 있는 코드
} catch (error) {
// 예외 발생 시 실행되는 코드
} finally {
// 예외 발생 여부와 상관없이 항상 실행
}
🔸 finally 블록의 특징
- finally 블록은 예외 발생 여부와 상관없이 항상 실행된다.
- 예외가 발생한 경우 → try → catch → finally
- 예외가 발생하지 않은 경우 → try → finally
const foo = (value) => {
if (value < 3) throw value;
else console.log(value);
};
const bar = (value) => {
try {
foo(value);
} catch (catchId) {
console.log('catch:', catchId);
return catchId;
} finally {
console.log('finally');
}
};
bar(2);
// 출력:
// catch: 2
// finally
🧠 정리
- catch 블록은 예외가 발생했을 때만 실행된다.
- finally 블록은 항상 실행되어 리소스 정리, 로그 기록, 연결 해제 등에 자주 활용된다.
📘 전체 요약
구분설명예시
| ECMAScript Error | 자바스크립트 문법·논리 오류 | 변수, 타입, 문법 등 |
| DOMException | 브라우저 API 동작 오류 | 네트워크, 요청 시간 초과 등 |
| try...catch | 예외를 처리하고 프로그램 종료 방지 | try → catch → finally |
✨ TIP
- try...catch를 사용하면 예외가 발생해도 프로그램이 중단되지 않는다.
- finally 블록은 예외 여부와 관계없이 반드시 실행되므로,
파일 닫기나 네트워크 해제 같은 “마무리 작업”에 적합하다.
객체 (Objects)
개념 설명
객체는 자바스크립트의 핵심 데이터 타입으로, 관련된 데이터와 기능을 하나로 묶어서 관리하는 복합 자료구조입니다. 자바스크립트에서 거의 모든 것은 객체이거나 객체처럼 동작합니다. 객체는 키(key)와 값(value)의 쌍으로 이루어진 프로퍼티(property)들의 집합이며, 값으로 함수를 가질 수 있는데 이를 메서드(method)라고 합니다.
객체의 특징
- 동적 프로퍼티: 실행 중에 프로퍼티 추가/삭제 가능
- 참조 타입: 객체는 메모리 주소를 참조
- 유연한 구조: 다양한 타입의 값을 포함 가능
- 프로토타입 기반: 다른 객체로부터 속성을 상속
객체 생성 방법
// 1. 객체 리터럴 (가장 일반적)
const person = {
name: "홍길동",
age: 30,
isStudent: false,
hobbies: ["독서", "운동"],
address: {
city: "서울",
country: "한국"
},
greet: function() {
console.log(`안녕하세요, ${this.name}입니다.`);
},
// ES6 메서드 단축 표기
introduce() {
console.log(`저는 ${this.age}살입니다.`);
}
};
// 2. new Object() 생성자
const car = new Object();
car.brand = "Tesla";
car.model = "Model 3";
car.year = 2024;
// 3. 생성자 함수 (ES5 방식)
function Dog(name, breed) {
this.name = name;
this.breed = breed;
this.bark = function() {
console.log("멍멍!");
};
}
const myDog = new Dog("바둑이", "진돗개");
// 4. Object.create() - 프로토타입 기반 생성
const animal = {
speak() {
console.log("소리를 냅니다");
}
};
const cat = Object.create(animal);
cat.name = "나비";
cat.speak(); // 프로토타입에서 상속받은 메서드
// 5. 클래스 (ES6)
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
프로퍼티 접근과 조작
const user = {
firstName: "김",
lastName: "철수",
age: 25
};
// 프로퍼티 접근
console.log(user.firstName); // 점 표기법
console.log(user["lastName"]); // 대괄호 표기법
console.log(user["first" + "Name"]); // 동적 접근
// 프로퍼티 추가
user.email = "user@example.com";
user["phone"] = "010-1234-5678";
// 프로퍼티 수정
user.age = 26;
// 프로퍼티 삭제
delete user.phone;
// 프로퍼티 존재 확인
console.log("email" in user); // true
console.log(user.hasOwnProperty("age")); // true
계산된 프로퍼티명 (Computed Property Names)
const propName = "score";
const obj = {
[propName]: 100, // score: 100
["user" + "Name"]: "홍길동", // userName: "홍길동"
[Symbol("id")]: 123 // 심볼 키
};
프로퍼티 축약 표기 (Property Shorthand)
const name = "이순신";
const age = 45;
// ES5 방식
const person1 = {
name: name,
age: age
};
// ES6 축약 방식
const person2 = { name, age }; // 변수명과 프로퍼티명이 같을 때
주요 Object 메서드
const student = {
name: "김학생",
age: 20,
grade: "A",
major: "컴퓨터공학"
};
// 1. Object.keys() - 키 배열 반환
console.log(Object.keys(student));
// ["name", "age", "grade", "major"]
// 2. Object.values() - 값 배열 반환
console.log(Object.values(student));
// ["김학생", 20, "A", "컴퓨터공학"]
// 3. Object.entries() - [키, 값] 쌍의 배열 반환
console.log(Object.entries(student));
// [["name", "김학생"], ["age", 20], ...]
// 4. Object.assign() - 객체 병합 (얕은 복사)
const defaults = { theme: "light", lang: "ko" };
const userSettings = { theme: "dark" };
const settings = Object.assign({}, defaults, userSettings);
// { theme: "dark", lang: "ko" }
// 5. Object.freeze() - 객체 동결 (수정 불가)
Object.freeze(student);
student.age = 21; // 무시됨 (엄격 모드에서는 에러)
// 6. Object.seal() - 프로퍼티 추가/삭제 금지 (수정은 가능)
Object.seal(student);
// 7. Object.getOwnPropertyDescriptor() - 프로퍼티 속성 확인
console.log(Object.getOwnPropertyDescriptor(student, "name"));
// { value: "김학생", writable: true, enumerable: true, configurable: true }
// 8. Object.defineProperty() - 프로퍼티 정의
Object.defineProperty(student, "id", {
value: 12345,
writable: false, // 수정 불가
enumerable: true, // 열거 가능
configurable: false // 재정의 불가
});
객체 복사
// 얕은 복사 (shallow copy)
const original = { a: 1, b: { c: 2 } };
const copy1 = Object.assign({}, original);
const copy2 = { ...original }; // 스프레드 연산자
original.b.c = 3;
console.log(copy1.b.c); // 3 (중첩 객체는 참조 복사)
// 깊은 복사 (deep copy)
const deepCopy = JSON.parse(JSON.stringify(original));
// 또는 structuredClone (최신 방법)
const deepCopy2 = structuredClone(original);
정규표현식 (Regular Expression)
생성 방법
// 1. 리터럴 방식 (권장)
const regex1 = /패턴/플래그;
const regex2 = /[a-z]+/gi;
// 2. 생성자 방식
const regex3 = new RegExp("패턴", "플래그");
주요 플래그
- g: 전역 검색 (global)
- i: 대소문자 구분 안 함 (ignore case)
- m: 여러 줄 검색 (multiline)
주요 메서드
// RegExp 메서드
regex.test(str); // true/false 반환
regex.exec(str); // 매치 결과 배열 반환
// String 메서드
str.match(regex); // 매치 결과 배열
str.replace(regex, replacement); // 문자열 치환
str.search(regex); // 매치 인덱스 반환
str.split(regex); // 문자열 분할
주요 메타 문자
- .: 임의의 한 문자
- ^: 문자열 시작
- $: 문자열 끝
- *: 0회 이상 반복
- +: 1회 이상 반복
- ?: 0회 또는 1회
- [abc]: a, b, c 중 하나
- [^abc]: a, b, c가 아닌 문자
- \d: 숫자 [0-9]
- \w: 단어 문자 [A-Za-z0-9_]
- \s: 공백 문자
컬렉션 (Collections)
개념 설명
컬렉션은 여러 개의 데이터를 효율적으로 저장하고 관리하기 위한 자료구조입니다. 자바스크립트는 Array(배열), Set(집합), Map(맵), WeakSet, WeakMap 등 다양한 컬렉션 타입을 제공하며, 각각의 특성에 맞게 데이터를 조직화할 수 있습니다.
Array (배열)
배열의 특징
- 순서가 있는 컬렉션: 인덱스를 통한 접근
- 동적 크기: 자동으로 크기 조절
- 다양한 타입: 여러 타입의 값을 함께 저장 가능
- 이터러블: for...of 루프 사용 가능
// 배열 생성
const arr1 = [1, 2, 3, 4, 5];
const arr2 = new Array(5); // 길이 5인 빈 배열
const arr3 = Array.of(1, 2, 3); // [1, 2, 3]
const arr4 = Array.from("hello"); // ['h', 'e', 'l', 'l', 'o']
// 요소 추가/제거
const fruits = ["사과", "바나나"];
fruits.push("오렌지"); // 끝에 추가 → ["사과", "바나나", "오렌지"]
fruits.pop(); // 끝에서 제거 → "오렌지"
fruits.unshift("딸기"); // 앞에 추가 → ["딸기", "사과", "바나나"]
fruits.shift(); // 앞에서 제거 → "딸기"
// 배열 탐색
const numbers = [10, 20, 30, 40, 50];
numbers.indexOf(30); // 2 (인덱스)
numbers.includes(40); // true
numbers.find(x => x > 25); // 30 (조건 만족하는 첫 요소)
numbers.findIndex(x => x > 25); // 2 (인덱스)
// 배열 변형
numbers.slice(1, 3); // [20, 30] (원본 유지)
numbers.splice(2, 1, 35); // [30] 제거, 35 삽입 (원본 변경)
numbers.concat([60, 70]); // 배열 합치기
numbers.reverse(); // 배열 뒤집기 (원본 변경)
numbers.sort((a, b) => a - b); // 정렬 (원본 변경)
// 고차 함수 (함수형 프로그래밍)
const nums = [1, 2, 3, 4, 5];
// map: 각 요소를 변환
nums.map(x => x * 2); // [2, 4, 6, 8, 10]
// filter: 조건에 맞는 요소만 선택
nums.filter(x => x % 2 === 0); // [2, 4]
// reduce: 배열을 하나의 값으로 축약
nums.reduce((sum, x) => sum + x, 0); // 15
// forEach: 각 요소에 대해 함수 실행
nums.forEach((x, index) => {
console.log(`${index}: ${x}`);
});
// some: 하나라도 조건을 만족하면 true
nums.some(x => x > 3); // true
// every: 모두 조건을 만족하면 true
nums.every(x => x > 0); // true
// 메서드 체이닝
const result = nums
.filter(x => x % 2 === 0) // 짝수만
.map(x => x * 2) // 2배로
.reduce((sum, x) => sum + x); // 합계
console.log(result); // 12
Set (집합)
Set의 특징
- 중복 불허: 같은 값은 한 번만 저장
- 순서 유지: 삽입 순서대로 순회
- 값의 동등성: === 연산자로 비교 (NaN은 예외)
- 빠른 검색: has() 메서드가 배열보다 빠름
// Set 생성
const set = new Set([1, 2, 3, 3, 4]); // Set(4) {1, 2, 3, 4}
const emptySet = new Set();
// 요소 추가/제거
set.add(5); // 5 추가
set.add(1); // 중복이므로 무시됨
set.delete(2); // 2 제거
set.has(3); // true (존재 확인)
set.size; // 4 (크기)
set.clear(); // 모든 요소 제거
// 배열의 중복 제거
const numbers = [1, 2, 2, 3, 3, 3, 4];
const uniqueNumbers = [...new Set(numbers)]; // [1, 2, 3, 4]
// Set 순회
const colors = new Set(["red", "green", "blue"]);
for (let color of colors) {
console.log(color);
}
colors.forEach(color => {
console.log(color);
});
// Set 연산
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
// 합집합
const union = new Set([...setA, ...setB]);
// Set(6) {1, 2, 3, 4, 5, 6}
// 교집합
const intersection = new Set(
[...setA].filter(x => setB.has(x))
);
// Set(2) {3, 4}
// 차집합
const difference = new Set(
[...setA].filter(x => !setB.has(x))
);
// Set(2) {1, 2}
Map (맵)
Map의 특징
- 키-값 쌍 저장: 객체와 유사하지만 더 강력
- 모든 타입의 키: 객체, 함수, 원시값 모두 키로 사용 가능
- 순서 보장: 삽입 순서대로 순회
- 크기 추적: size 프로퍼티로 쉽게 확인
- 이터러블: for...of로 순회 가능
// Map 생성
const map = new Map();
const map2 = new Map([
["name", "홍길동"],
["age", 30],
["city", "서울"]
]);
// 요소 추가/조회/삭제
map.set("key1", "value1");
map.set("key2", "value2");
map.get("key1"); // "value1"
map.has("key1"); // true
map.delete("key1"); // true
map.size; // 1
map.clear(); // 모든 요소 제거
// 객체를 키로 사용
const obj1 = { id: 1 };
const obj2 = { id: 2 };
const objMap = new Map();
objMap.set(obj1, "첫 번째 객체");
objMap.set(obj2, "두 번째 객체");
console.log(objMap.get(obj1)); // "첫 번째 객체"
// 함수를 키로 사용
const funcMap = new Map();
function greet() { return "Hello"; }
funcMap.set(greet, "인사 함수");
// Map 순회
const userMap = new Map([
["name", "김철수"],
["age", 25],
["job", "개발자"]
]);
// 1. for...of로 [키, 값] 순회
for (let [key, value] of userMap) {
console.log(`${key}: ${value}`);
}
// 2. forEach 사용
userMap.forEach((value, key) => {
console.log(`${key}: ${value}`);
});
// 3. keys(), values(), entries() 사용
for (let key of userMap.keys()) {
console.log(key);
}
for (let value of userMap.values()) {
console.log(value);
}
for (let [key, value] of userMap.entries()) {
console.log(key, value);
}
// Map vs Object
// Object: 문자열/심볼만 키로 사용, 프로토타입 체인
// Map: 모든 값을 키로 사용, 순서 보장, size 프로퍼티
WeakMap과 WeakSet
약한 참조의 개념
WeakMap과 WeakSet은 "약한(weak)" 참조를 유지하므로, 다른 참조가 없으면 가비지 컬렉션 대상이 됩니다.
// WeakMap - 객체만 키로 사용 가능
const weakMap = new WeakMap();
let obj = { data: "중요한 데이터" };
weakMap.set(obj, "관련 정보");
// obj가 null이 되면 WeakMap의 항목도 자동 제거
obj = null; // 가비지 컬렉션 대상
// WeakMap 활용 - 비공개 데이터 저장
const privateData = new WeakMap();
class User {
constructor(name, password) {
this.name = name;
privateData.set(this, { password }); // 비공개 정보
}
checkPassword(pwd) {
return privateData.get(this).password === pwd;
}
}
// WeakSet - 객체만 저장 가능
const weakSet = new WeakSet();
const obj1 = { id: 1 };
weakSet.add(obj1);
weakSet.has(obj1); // true
JSON (JavaScript Object Notation)
JSON 특징
- 데이터 교환을 위한 텍스트 포맷
- 언어 독립적인 표준 형식
- 키는 반드시 큰따옴표로 감싸야 함
- 문자열도 큰따옴표만 사용 (작은따옴표 불가)
- 주석 사용 불가
- undefined, NaN, Infinity 사용 불가
JSON vs JavaScript 객체
// JavaScript 객체
const jsObj = {
name: 'John', // 작은따옴표 가능
age: 30
};
// JSON 형식
const jsonStr = '{"name":"John","age":30}'; // 큰따옴표 필수
주요 메서드
// 객체 → JSON 문자열
const jsonString = JSON.stringify(object);
JSON.stringify(obj, null, 2); // 들여쓰기 2칸
// JSON 문자열 → 객체
const object = JSON.parse(jsonString);
JSON.stringify 옵션
// replacer: 특정 속성만 직렬화
JSON.stringify(obj, ['name', 'age']);
// space: 들여쓰기
JSON.stringify(obj, null, 2);
// 무시되는 값
JSON.stringify({
func: function() {}, // 함수 무시
sym: Symbol('id'), // 심볼 무시
undef: undefined // undefined 무시
}); // 결과: "{}"
국제화 (Internationalization)
Intl.DateTimeFormat
const date = new Date();
// 날짜 형식
new Intl.DateTimeFormat('ko-KR').format(date);
new Intl.DateTimeFormat('en-US').format(date);
// 옵션 설정
new Intl.DateTimeFormat('ko-KR', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long'
}).format(date);
Intl.NumberFormat
const number = 1234567.89;
// 숫자 형식
new Intl.NumberFormat('ko-KR').format(number);
new Intl.NumberFormat('en-US').format(number);
// 통화 형식
new Intl.NumberFormat('ko-KR', {
style: 'currency',
currency: 'KRW'
}).format(number);
Intl.Collator
// 문자열 정렬
const collator = new Intl.Collator('ko-KR');
['바', '가', '나'].sort(collator.compare);
프로토타입 (Prototype)
프로토타입 체인
function Person(name) {
this.name = name;
}
// 프로토타입에 메서드 추가
Person.prototype.greet = function() {
console.log(`안녕, 나는 ${this.name}이야`);
};
const person1 = new Person("홍길동");
person1.greet(); // 프로토타입 메서드 호출
프로토타입 상속
function Student(name, grade) {
Person.call(this, name); // 부모 생성자 호출
this.grade = grade;
}
// 프로토타입 체인 설정
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
// 메서드 추가
Student.prototype.study = function() {
console.log(`${this.name}이(가) 공부 중입니다.`);
};
프로토타입 확인
obj.__proto__; // 프로토타입 접근 (비권장)
Object.getPrototypeOf(obj); // 프로토타입 가져오기 (권장)
obj instanceof Constructor; // 인스턴스 확인
Constructor.prototype.isPrototypeOf(obj); // 프로토타입 체인 확인
Class 문법
기본 클래스
class Person {
// 생성자
constructor(name, age) {
this.name = name;
this.age = age;
}
// 메서드
greet() {
console.log(`안녕, 나는 ${this.name}이야`);
}
// 정적 메서드
static species() {
return "Homo Sapiens";
}
// getter
get info() {
return `${this.name} (${this.age}세)`;
}
// setter
set info(value) {
const [name, age] = value.split(',');
this.name = name;
this.age = parseInt(age);
}
}
상속
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 부모 생성자 호출
this.grade = grade;
}
// 메서드 오버라이딩
greet() {
super.greet(); // 부모 메서드 호출
console.log(`나는 ${this.grade}학년이야`);
}
}
비공개 필드 (Private Fields)
class BankAccount {
#balance = 0; // private 필드
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
this와 화살표 함수
this 바인딩 규칙
// 1. 전역 컨텍스트
console.log(this); // window (브라우저) 또는 global (Node.js)
// 2. 메서드 호출
const obj = {
name: "객체",
method() {
console.log(this.name); // "객체"
}
};
// 3. 일반 함수 호출
function func() {
console.log(this); // window (비엄격) / undefined (엄격)
}
// 4. 생성자 함수
function Constructor() {
this.value = 10;
}
const instance = new Constructor(); // this는 새 객체
명시적 바인딩
const obj = { name: "객체" };
function greet() {
console.log(this.name);
}
greet.call(obj); // this를 obj로 즉시 호출
greet.apply(obj); // call과 유사 (인자 배열)
const bound = greet.bind(obj); // this가 고정된 새 함수 반환
화살표 함수
// 화살표 함수는 자신의 this를 가지지 않음
// 상위 스코프의 this를 그대로 사용 (렉시컬 this)
const obj = {
name: "객체",
// 일반 함수
regularFunc: function() {
console.log(this.name); // "객체"
setTimeout(function() {
console.log(this.name); // undefined (this가 window)
}, 100);
},
// 화살표 함수
arrowFunc: function() {
console.log(this.name); // "객체"
setTimeout(() => {
console.log(this.name); // "객체" (상위 this 유지)
}, 100);
}
};
화살표 함수 특징
- 간결한 문법
- this 바인딩 없음 (상위 스코프의 this 사용)
- arguments 객체 없음
- 생성자로 사용 불가 (new 불가)
- prototype 속성 없음
// 간결한 문법
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => console.log("Hello");
// 객체 반환 시 괄호 필요
const makeObj = (name, age) => ({ name, age });
스코프 (Scope)
스코프 종류
// 1. 전역 스코프
var globalVar = "전역";
// 2. 함수 스코프
function outer() {
var functionVar = "함수";
console.log(functionVar); // 접근 가능
}
// console.log(functionVar); // 에러: 접근 불가
// 3. 블록 스코프 (let, const)
{
let blockVar = "블록";
const blockConst = "블록 상수";
console.log(blockVar); // 접근 가능
}
// console.log(blockVar); // 에러: 접근 불가
var vs let vs const
// var: 함수 스코프, 호이스팅, 재선언 가능
var x = 1;
var x = 2; // 재선언 가능
console.log(x); // 2
// let: 블록 스코프, 호이스팅(TDZ), 재선언 불가
let y = 1;
// let y = 2; // 에러: 재선언 불가
y = 2; // 재할당 가능
// const: 블록 스코프, 호이스팅(TDZ), 재선언/재할당 불가
const z = 1;
// z = 2; // 에러: 재할당 불가
// 단, 객체/배열의 내부는 변경 가능
const arr = [1, 2, 3];
arr.push(4); // 가능
렉시컬 스코프
// 함수가 정의된 위치에 따라 스코프 결정
const x = 1;
function outer() {
const x = 10;
inner();
}
function inner() {
console.log(x); // 1 (정의된 위치의 스코프)
}
outer();
클로저 (Closure)
클로저 개념
함수가 선언될 때의 렉시컬 환경을 기억하여, 함수가 스코프 밖에서 실행될 때도 그 환경에 접근할 수 있는 것
function outerFunction(x) {
// 외부 함수의 변수
return function innerFunction(y) {
// 내부 함수에서 외부 함수 변수에 접근 (클로저)
return x + y;
};
}
const addFive = outerFunction(5);
console.log(addFive(3)); // 8
console.log(addFive(10)); // 15
클로저 활용
1. 데이터 은닉 (캡슐화)
function createCounter() {
let count = 0; // private 변수
return {
increment() {
count++;
return count;
},
decrement() {
count--;
return count;
},
getCount() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
// console.log(counter.count); // undefined (접근 불가)
2. 팩토리 함수
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
3. 이벤트 핸들러
function setupButtons() {
for (let i = 1; i <= 3; i++) {
const button = document.createElement('button');
button.textContent = `Button ${i}`;
// 클로저로 i 값 유지
button.addEventListener('click', function() {
console.log(`Button ${i} clicked`);
});
document.body.appendChild(button);
}
}
클로저 주의사항
// 잘못된 예: var 사용 시 문제
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // 3, 3, 3 (모두 3)
}, 100);
}
// 해결 방법 1: let 사용
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // 0, 1, 2
}, 100);
}
// 해결 방법 2: IIFE 사용
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // 0, 1, 2
}, 100);
})(i);
}
실행 컨텍스트 (Execution Context)
실행 컨텍스트란?
코드가 실행되기 위해 필요한 환경 정보를 담고 있는 객체
실행 컨텍스트 구성
실행 컨텍스트
├── Variable Environment (변수 환경)
│ ├── Environment Record (환경 레코드)
│ └── Outer Environment Reference (외부 환경 참조)
├── Lexical Environment (렉시컬 환경)
│ ├── Environment Record
│ └── Outer Environment Reference
└── ThisBinding (this 바인딩)
실행 컨텍스트 생성 과정
var x = 10;
function foo() {
var y = 20;
console.log(x + y);
}
foo();
// 1. 전역 실행 컨텍스트 생성
// 2. foo 함수 호출 시 함수 실행 컨텍스트 생성
// 3. 컨텍스트 스택에 푸시
// 4. 함수 실행 완료 후 컨텍스트 스택에서 팝
호이스팅 (Hoisting)
변수 및 함수 선언이 스코프의 최상단으로 끌어올려지는 것처럼 동작
// 변수 호이스팅
console.log(x); // undefined (선언만 호이스팅)
var x = 10;
// 위 코드는 다음과 같이 해석됨
var x;
console.log(x);
x = 10;
// 함수 호이스팅
foo(); // "Hello" (함수 전체가 호이스팅)
function foo() {
console.log("Hello");
}
// let/const의 TDZ (Temporal Dead Zone)
// console.log(y); // ReferenceError
let y = 20;
스코프 체인
var a = 1;
function outer() {
var b = 2;
function inner() {
var c = 3;
console.log(a, b, c); // 1 2 3
// 스코프 체인: inner → outer → global
}
inner();
}
outer();
실행 컨텍스트 스택 (Call Stack)
function first() {
console.log("첫 번째");
second();
console.log("첫 번째 종료");
}
function second() {
console.log("두 번째");
third();
console.log("두 번째 종료");
}
function third() {
console.log("세 번째");
}
first();
// Call Stack 변화:
// 1. [global]
// 2. [global, first]
// 3. [global, first, second]
// 4. [global, first, second, third]
// 5. [global, first, second]
// 6. [global, first]
// 7. [global]
추가 참고사항
메모리 관리
// 클로저 사용 시 메모리 누수 주의
function createHeavyObject() {
const heavyData = new Array(1000000);
return function() {
// heavyData가 계속 메모리에 유지됨
return heavyData.length;
};
}
// 해결: 필요없는 참조 제거
function createOptimizedObject() {
const heavyData = new Array(1000000);
const length = heavyData.length;
return function() {
return length; // 배열 전체 대신 길이만 저장
};
}
디버깅 팁
// console.log 활용
console.log('변수:', variable);
console.table([{a:1, b:2}, {a:3, b:4}]);
console.dir(object);
// debugger 문
function debug() {
debugger; // 여기서 중단점
console.log("디버깅");
}
// 스택 트레이스
console.trace();