
1. Javascript 기본 문법
1.1 선언하기
- body 혹은 head 영역 안에 <script> </script> 태그로 선언하면 된다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
console.log("Hello, World!");
</script>
</body>
</html>
1.2 변수 키워드
1) const (기본 추천)
- 재할당이 불가능하기 때문에 재할당이 필요 없을 때 사용한다.
const name = "Seoul";
const MAX_COUNT = 120;
const a = 1;
a = 2; // TypeError
- 하지만 객체/배열은 내용 변경이 가능하다(참조는 고정).
const arr = [1, 2];
arr.push(3); // OK
const obj = { x: 1 };
obj.x = 2; // OK
2) let
- 값을 바꿔야 할 때(재할당 필요) 사용한다. 중복 선언은 불가능하다.
let count = 0;
count = count + 1;
3) var (가능하면 피하기)
- 오래된 방식이고 let/const와 달리 함수 스코프, var만의 호이스팅 특성 때문에 버그를 만들기 쉽다. 가능한 피하는 게 좋다.
var x = 1;
4) 선언만 하고 나중에 할당
- let나 var과 달리 const는 선언과 동시에 초기화를 해야한다.
let selectedRegion; // undefined
selectedRegion = "Jongno";
//const는 선언과 동시에 초기화가 필요하다.
const x; // SyntaxError
간단하게 정리하면 다음과 같다.
- 기본은 const
- 값 바뀌면 let
- var는 사용하지 않기
1.3 호이스팅(hoisting)
- 자바스크립트에서 호이스팅(Hoisting)은 코드가 실행되기 전에 변수와 함수의 선언부가 스코프(유효 범위)의 최상단으로 끌어올려지는 듯한 현상을 말한다.
- 자바스크립트 엔진이 코드를 실행하기 전 메모리 할당 단계에서 선언된 식별자(변수명, 함수명)들을 미리 수집하기 때문에 발생하는 현상이다.
- var 변수의 호이스팅 : var로 선언한 변수는 호이스팅 시 undefined로 초기화된다. 즉, 선언 줄보다 먼저 변수를 참조해도 에러가 나지 않고 undefined가 나온다.
console.log(text); // undefined (에러 발생 X)
var text = "Hello";
console.log(text); // "Hello"
- 내부 동작 원리:
- 생성 단계: 엔진이 text라는 변수를 발견하고 메모리에 공간을 확보한 뒤, 즉시 undefined를 넣는다.
- 실행 단계: console.log를 만났을 때 이미 text는 undefined를 가지고 있다. 그 후 text = "Hello" 할당문이 실행된다.
1) 함수 선언문 (Function Declaration)
- function 키워드로 만든 함수 선언문은 선언과 내용(body) 전체가 호이스팅된다. 따라서 함수를 정의하기 전 위치에서 호출해도 정상 작동한다.
greet(); // "안녕하세요!" (작동함)
function greet() {
console.log("안녕하세요!");
}
2) let과 const의 호이스팅 (TDZ의 존재)
- let과 const도 호이스팅이 된다. 하지만 var와 다르게 동작한다. let와 const는 스코프 시작점부터 변수 선언 줄까지 일시적 사각지대(TDZ, Temporal Dead Zone)에 빠지게 된다. 이 구간에서 변수를 참조하면 ReferenceError가 발생한다.
console.log(name); // ReferenceError: Cannot access 'name' before initialization
let name = "Gemini";
- 내부 동작 원리:
- 생성 단계: 엔진이 name이라는 변수를 발견하고 호이스팅(등록)한다. 하지만 var처럼 undefined로 초기화하지 않고, "초기화 전 상태"로 남겨둔다.
- 실행 단계: let name 선언문을 만나기 전까지는 메모리에 접근을 차단한다(TDZ). 따라서 선언 전에 접근하면 에러가 발생한다.
3) 함수 표현식 (Function Expression)
- 함수를 변수에 담아서 만드는 방식(함수 표현식)은 변수 호이스팅 규칙을 따른다.
// var 사용 시
myFunc(); // TypeError: myFunc is not a function
var myFunc = function() { ... };
// 이유: myFunc가 undefined로 호이스팅 되었기 때문에, undefined를 함수처럼 호출하려 해서 에러 발생
// const 사용 시
myConstFunc(); // ReferenceError
const myConstFunc = function() { ... };
// 이유: TDZ에 걸림
1.4 변수의 명명 규칙
변수의 명명 규칙은 다른 언어와 비슷하다.
- 알파벳(a~z, A~Z), 숫자(0~9), 밑줄(_), 달러 기호($)가 사용 가능
- 첫 글자로 숫자 사용 불가.
- 예약어 사용 불가
1.5 데이터의 표현
자바스크립트의 데이터 타입은 크게 원시 타입(Primitive Type)과 참조 타입(Reference Type) 두 가지로 나뉜다.
자바스크립트는 동적 타입 언어(Dynamically Typed Language)이기 때문에 변수를 선언할 때 타입을 미리 지정하지 않는다. 변수에 어떤 값이 할당되느냐에 따라 타입이 자동으로 결정된다.
1) 원시 타입 (Primitive Types)
데이터의 실제 값이 변수에 직접 저장된다. 값 자체는 변경 불가능(Immutable)하고 작고 단순한 데이터들이다.
- Number (숫자): 정수와 실수를 구분하지 않고 모두 실수로 처리한다. 특수 값에는 Infinity (무한대), NaN (Not a Number, 계산 불가능)이 있다.
const age = 30; const pi = 3.14; - String (문자열): 텍스트 데이터다. 작은따옴표(''), 큰따옴표(""), 백틱(``)으로 감싼다.
const name = "javascript"; const greeting = `Hello, ${name}`; // 템플릿 리터럴 - Boolean (불리언): 논리적인 참(true)과 거짓(false)만 나타낸다. 조건문에서 주로 쓰인다.
const isLogin = true; - undefined: 변수가 선언되었지만, 아직 값이 할당되지 않은 상태다.
let user; console.log(user); // undefined - null: 변수에 값이 '없음'을 명시적으로 표현할 때 사용한다.
let selection = null; // 선택된 게 없음을 명시 - Symbol (심볼): ES6에서 추가됐다. 고유하고 변경 불가능한 식별자를 만들 때 사용힌다.
- BigInt: 아주 큰 정수를 다룰 때 사용한다. (2^53 - 1보다 큰 수)
2) 참조 타입 (객체 타입, Reference Types / Object Types)
데이터의 주소(참조 값)가 변수에 저장된다. 실제 데이터는 메모리의 힙(Heap) 영역에 저장된다. Object (일반 객체 리터럴 {} 포함), Array, Function, Date, RegExp, Map / Set / WeakMap / WeakSet, Error (Error, TypeError 등), Promise, Class 인스턴스 (사용자 정의 클래스/생성자 함수로 만든 객체), TypedArray / ArrayBuffer / DataView (예: Uint8Array), DOM 관련 객체 (브라우저 환경: Element, Document 등), 기타 내장 객체 등 다양한 객체들이 있지만 일단 먼저 대표적인 3가지만 알아보겠다.
- Object (객체): 키(Key)와 값(Value)의 쌍으로 이루어진 데이터 집합이다.
const user = { name: "Javascript", age: 3, isValid: true }; - Array (배열): 데이터를 순서대로 나열한 리스트다.
const fruits = ["Apple", "Banana", "Cherry"]; console.log(fruits[0]); // Apple - Function (함수): 실행 가능한 코드의 묶음이다. 자바스크립트에서는 함수도 값으로 취급되어 변수에 담을 수 있다.
const add = function(a, b) { return a + b; };
3) typeof 연산자로 타입 확인하기
변수에 들어있는 데이터의 타입을 알고 싶을 때는 typeof를 사용한다.
console.log(typeof 123); // "number"
console.log(typeof "Text"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof { x: 1 }); // "object"
console.log(typeof [1, 2, 3]); // "object" (배열도 객체로 나옴)
// 자바스크립트의 유명한 버그
console.log(typeof null); // "object" (원래는 null이어야 맞음)
1.6 주석
1) 한 줄 주석(Single line Command)
슬래시 두 개를 사용한다.
const name = "React"; // 코드 뒤에 이렇게 설명을 붙인다.
// console.log("이 코드는 실행되지 않는다.");
console.log("이 코드는 실행된다.");
2) 여러 줄 주석 (Multi-line Comment)
슬래시와 별표(/* ... */)를 사용한다. 시작(/*)과 끝(*/) 사이의 모든 내용을 무시한다. 긴 설명을 적거나 코드 덩어리를 통째로 끄고 싶을 때 쓴다.
/*
이곳은 여러 줄 주석 영역이다.
작성자: 지민
날짜: 2024-05-20
목적: 복잡한 계산 로직 설명
*/
const age = 25;
/* console.log("여기서부터");
console.log("여기까지");
console.log("전부 실행 안 됨");
*/
참고로 vs code에서는 Ctrl 과 / 를 동시에 누르거나 Cmd 와 / 를 동시에 누르면 한 번에 주석처리 할 수 있다.
1.7 변수의 출력
자바스크립트에서 “변수의 출력”은 크게 콘솔 출력, 화면(DOM) 출력, 팝업 출력, 문자열로 조합해서 출력 네 가지로 정리하는 게 가장 실용적이다.
1) 콘솔에 출력하기
- console.log()
let x = 10;
console.log(x); // 10
console.log("x =", x); // x = 10
- console.error()
- console.warn()
- console.table()
console.error("에러");
console.warn("경고");
const users = [{id:1, name:"Kim"}, {id:2, name:"Lee"}];
console.table(users); // 표 형태로 보기 좋게 출력
2) 웹 페이지 화면에 출력하기
- 간단하게 바로 출력하기 : document.write
<!doctype html>
<html>
<head><meta charset="utf-8"><title>document.write</title></head>
<body>
<script>
const name = "철수";
document.write("안녕하세요, " + name + "님");
</script>
</body>
</html>
- 특정 요소에 텍스트 넣기: textContent
- textContent는 문자 그대로 넣기 때문에 안전하다(HTML 해석 안 함).
<div id="result"></div>
<script>
const x = 10;
document.querySelector("#result").textContent = x;
</script>
- HTML로 넣기: innerHTML
- 문자열이 사용자 입력을 포함하면 XSS (Cross Site Scripting) 위험이 생길 수 있어 주의가 필요하다.
document.querySelector("#result").innerHTML = "<b>" + x + "</b>";
3) 팝업으로 출력하기
- alert()
const x = 10;
alert(x);
4) 문자열로 예쁘게 출력하기 (템플릿 리터럴)
- +로 이어 붙이는 것보다 가독성이 좋다.
const name = "철수";
const age = 20;
console.log(`${name}의 나이는 ${age}살입니다.`);
5) 여러 값을 “구분해서” 출력하기
console.log("A", 1, true, {x: 1}); // 공백으로 구분되어 출력
6) 객체/배열 출력 시 주의점
- 객체를 문자열로 강제 변환하면 깨질 수 있어 주의가 필요하다.
const obj = { a: 1 };
console.log("obj = " + obj); // obj = [object Object]
console.log(obj); // { a: 1 } (이게 정상적으로 보기 좋음)
- JSON 문자열로 보고 싶으면
console.log(JSON.stringify(obj)); // {"a":1}
console.log(JSON.stringify(obj, null, 2)); // 들여쓰기 포함
7) 디버깅용으로 변수 상태 추적
console.log({ x, name, age }); // 키 이름이 자동으로 붙어서 보기 편함
1.8 연산
1) 산술 연산
- 자바스크립트는 변수의 연산을 위해 기본적인 산술연산( +, -, *, /,% )을 지원한다.
- 다만 자바스크립트에서 파이썬과 같이 추가적으로 지원하는 연산이 있는데 바로 제곱연산(**)이다. 또한 제곱연산은 우측 결합성을 갖는다.( a ** b ** c는 a ** (b ** c) )
-2 ** 2;
// Bash에서는 4, 다른 언어에서는 -4
// 모호한 식은 자바스크립트에서 사용할 수 없다. 사용하면 SyntaxError가 발생한다.
-(2 ** 2);
// 자바스크립트에서 -4
2 ** 3; // 8
3 ** 2; // 9
3 ** 2.5; // 15.588457268119896
10 ** -1; // 0.1
NaN ** 2; // NaN
2 ** 3 ** 2; // 512
2 ** (3 ** 2); // 512
(2 ** 3) ** 2; // 64
2) 대입 연산과 증감 연산
- 대입 연산자와 복합 대입 연산자도 다른 언어와 같다
- =, +=, -=, *=, %=, **=
- 증감 연산자도 다른 언어처럼 전위 증감 연산자와 후위 증감 연산자가 있고 값 변경되는 순간도 다른 언어와 같이 전위는 반환 전, 후위는 반환 후다.
- ++x, --x, x++, x--
3) 문자 결합
- + 연산자 (가장 기본적인 방법)
const str1 = "Hello"; const str2 = "World"; const result = str1 + ", " + str2 + "!"; console.log(result); // "Hello, World!" - 주의: 암묵적 타입 변환 (Type Coercion)
console.log("Age: " + 25); // "Age: 25" (25가 문자열로 변환됨) console.log(10 + "20"); // "1020" (덧셈이 아닌 문자 결합이 일어남) console.log(10 + 20 + "30"); // "3030" (앞의 10+20은 숫자 계산되어 30, 뒤에는 문자열 결합) - 템플릿 리터럴 (Template Literals)
- ${ }, 안에서는 간단한 연산도 가능하다. 예: ${a + b}
- 성질을 간단하게 정리하면 다음과 같다.
- 변수 삽입: ${변수명} 형태로 문자열 중간에 변수나 표현식을 바로 넣을 수 있다.
- 멀티라인: 줄바꿈을 그대로 인식한다.
const name = "ai"; const version = 1.5; // 기존 방식 // const msg = "I am " + name + " and version is " + version; // 템플릿 리터럴 방식 const msg = `I am ${name} and version is ${version}`; console.log(msg); // "I am ai and version is 1.5" - concat() 메서드
const str1 = "Hello"; const str2 = "World"; const result = str1.concat(" ", str2); console.log(result); // "Hello World" - join() 메서드 (배열을 문자열로)
const words = ["Java", "Script", "Is", "Fun"]; const sentence = words.join(" "); // 구분자(공백)를 넣어 결합 console.log(sentence); // "Java Script Is Fun" - += (복합 할당 연산자)
let story = "옛날에 "; story += "한 개발자가 "; story += "살았습니다."; console.log(story); // "옛날에 한 개발자가 살았습니다."
4) 비교 연산
- 비교 연산자는 두 개의 데이터를 비교할 때 사용하고 연산의 결과로 참(true) 또는 거짓(false)인 논리형(Boolean) 데이터를 반환한다.
연산자 설명 예시 (a=10, b=20) 결과 A > B A가 B보다 크다 10 > 20 false A < B A가 B보다 작다 10 < 20 true A >= B A가 B보다 크거나 같다 10 >= 20 false A <= B A가 B보다 작거나 같다 10 <= 20 true - 동등 연산자 (==, !=) : 값만 비교 (느슨한 비교), 자료형(Type)이 달라도 표기된 숫자(값)만 일치하면 true를 반환한다. 자바스크립트 엔진이 자동으로 자료형을 변환하여 비교한다. ( 10 == '10' → true )
- 일치 연산자 (===, !==) : 값과 자료형 모두 비교 (엄격한 비교), 값뿐만 아니라 자료형(Type)까지 정확히 일치해야 true를 반환한다.
5) 논리 연산
- || : or 연산자. 값이 하나라도 true면 true로 결과값을 반환한다.
- && : and 연산자. 값이 하나라도 false면 false로 결과값을 반환한다.
- ! not 연산자. 값이 true면 false를 false면 true를 반환한다.
6) 비트 연산
- 숫자를 32비트 정수로 변환해서 비트별로 연산을 수행한다.
- & (AND): 두 비트가 모두 1일 때만 1을 반환한다.
- | (OR): 둘 중 하나라도 1이면 1을 반환한다.
- ^ (XOR): 두 비트가 다를 때만 1을 반환한다.
- ~ (NOT): 모든 비트를 반전시킨다 (0은 1로, 1은 0으로).
- << (Left Shift): 비트를 왼쪽으로 이동시키고, 오른쪽은 0으로 채운다. n번 시프트하면 2^n을 곱한 것과 같다.
- >> (Sign-propagating Right Shift): 비트를 오른쪽으로 이동시키고, 가장 왼쪽 비트(부호 비트)를 복제하여 채운다.
- 예: 10 >> 1 (1010 >> 1) -> 0101 (5)
- >>> (Zero-fill Right Shift): 비트를 오른쪽으로 이동시키고 항상 0으로 채운다. 양수에만 사용하고 부호 없는 결과를 얻는다.
6) 연산자 우선순위
| 18 | 그룹핑 | n/a | (x) |
| 17 | 접근·호출 | L→R | x.y, x?.y |
| 17 | 접근·호출 | n/a | x[y], new x(y), x(y), import(x) |
| 16 | new(인수 없음) | n/a | new x |
| 15 | 후위 단항 | n/a | x++, x-- |
| 14 | 전위 단항 | n/a | ++x, --x, !x, ~x, +x, -x, typeof x, void x, delete x, await x |
| 13 | 거듭제곱 | R→L | x ** y |
| 12 | 곱셈 | L→R | x * y, x / y, x % y |
| 11 | 덧셈 | L→R | x + y, x - y |
| 10 | 시프트 | L→R | x << y, x >> y, x >>> y |
| 9 | 관계 | L→R | x < y, x <= y, x > y, x >= y, x in y, x instanceof y |
| 8 | 동등 | L→R | x == y, x != y, x === y, x !== y |
| 7 | 비트 AND | L→R | x & y |
| 6 | 비트 XOR | L→R | x ^ y |
| 5 | 비트 OR | L→R | x | y |
| 4 | 논리 AND | L→R | x && y |
| 3 | 논리 OR / Nullish | L→R | x || y, x ?? y |
| 2 | 할당(대) | R→L | x = y, x += y, x -= y, x **= y, x *= y, x /= y, x %= y, x <<= y, x >>= y, x >>>= y, x &= y, x ^= y, x |= y, x &&= y, x ||= y, x ??= y |
| 2 | 조건(삼항) | R→L | x ? y : z |
| 2 | 화살표 | R→L | x => y |
| 2 | yield / spread | n/a | yield x, yield* x, ...x |
| 1 | 콤마 | L→R | x, y |
2. 제어문
제어문은 프로그램이 위에서 아래로 한 줄씩 실행되는 기본 흐름을 바꿔서
- 조건에 따라 분기하거나(선택)
- 특정 조건/횟수 동안 반복하고(반복)
- 반복 중간에 탈출/건너뛰기(흐름 제어)
를 가능하게 해주는 문장(statement)이다.
2.1 선택문(Conditionals): if, if~else, if~else if~else
1) if (단일 분기)
- 조건이 truthy(참처럼 평가되는 값)면 블록을 실행한다.
const score = 85;
if (score >= 60) {
console.log("합격");
}
- truthy / falsy : if (condition)에서 condition은 Boolean이 아니어도 되고 “참/거짓처럼” 평가된다. 예를 들어, falsy: false, 0, "", null, undefined, NaN. 나머지는 대부분 truthy
2) if ~ else (양자택일)
조건이 truthy면 if 블록, falsy면 else 블록이 실행된다.
const isMember = false;
if (isMember) {
console.log("회원가 적용");
} else {
console.log("일반가 적용");
}
3) if ~ else if ~ else (다중 분기)
여러 조건을 위에서 아래로 순서대로 검사하고, 처음으로 true가 된 블록 하나만 실행한다.
const temp = 31;
if (temp >= 30) {
console.log("폭염");
} else if (temp >= 20) {
console.log("따뜻");
} else if (temp >= 10) {
console.log("쌀쌀");
} else {
console.log("추움");
}
2.2. 반복문(Loops): for, while
1) for 반복문
for는 초기화; 조건; 증감의 3요소로 루프를 구성한다.
// 0부터 4까지 출력
for (let i = 0; i < 5; i++) {
console.log(i);
}
for 동작 순서
- 초기화(한 번)
- 조건 검사 → true면 본문 실행
- 증감 실행
- 다시 2)로 반복
2) while 반복문 (조건이 “참인 동안 계속”)
while은 조건을 먼저 검사하고, true인 동안 본문을 반복 실행한다.
let i = 0;
while (i < 5) {
console.log(i);
i++;
}
3) do...while
조건을 나중에 검사하기 때문에, 본문이 무조건 한 번은 실행된다.
let input;
do {
input = prompt("값을 입력하세요(취소 시 null)");
} while (input === "");
2.3 반복 제어: break, continue
1) break — “반복문(또는 switch) 즉시 종료”
현재 루프(또는 switch)를 완전히 끝내고 다음 문장으로 이동한다.
for (let i = 0; i < 10; i++) {
if (i === 3) break;
console.log(i); // 0,1,2까지만 출력
}
2) continue
현재 반복의 나머지 문장을 건너뛰고, 다음 회차로 넘어간다. (for는 증감식으로, while은 조건 검사로 점프)
for (let i = 0; i < 5; i++) {
if (i === 2) continue;
console.log(i); // 0,1,3,4 출력
}
2.4. switch 문: 다중 분기(else-if 체인 대안)
1) 기본 개념
- 값(상태 코드, 타입 문자열, 메뉴 선택 등)에 따라 여러 갈래로 분기할 때 else if 체인을 더 읽기 좋게 정리할 수 있다.
- 단, “범위 비교(>=, <)”처럼 조건식 위주면 if/else if가 더 자연스럽다.
switch (expression) {
case caseExpression1:
// statements
break;
case caseExpression2:
// statements
break;
default:
// statements
}
- switch문 동작
- switch는 먼저 expression을 1번 평가한다.
- 각 case는 strict equality(엄격 비교, ===)로 매칭된다. 즉, 타입 변환 없이 같은 값이어야 매치된다.
- 매칭되는 case를 찾으면 그 지점부터 실행을 시작하고, break를 만나거나 switch 끝까지 실행한다.
- 어떤 case도 매칭되지 않으면 default로 점프한다(있다면). default는 최대 1개만 가능하고 위치는 어디든 가능하지만 관례상 마지막에 둔다.
- case 표현식은 “필요할 때만” 평가된다. 이미 매칭이 확정되면 이후 case들의 표현식은 평가되지 않을 수 있다(그래도 fall-through로 실행은 될 수 있음).
2) fall-through(의도적/실수)와 break
- break를 빼먹으면 다음 case로 그대로 이어서 실행된다(이를 fall-through라고 한다.)
- 여러 case를 “그룹”으로 묶는 패턴. 이 패턴은 MDN 예시처럼 의도적인 fall-through로 “같은 처리”를 공유할 때 유용하다.
switch (fruit) {
case "Mangoes":
case "Papayas":
console.log("열대과일");
break;
default:
console.log("기타");
}
3) switch 내부의 let/const 스코프
case는 별도의 블록 스코프를 만들지 않는다. 그래서 서로 다른 case에 같은 이름의 const/let을 선언하면 충돌(SyntaxError)이 난다. 해결은 case를 블록 {}로 감싸는 것이다.
switch (action) {
case "say_hello": {
const message = "hello";
console.log(message);
break;
}
case "say_hi": {
const message = "hi";
console.log(message);
break;
}
}
2.5 for...of: “값(value)”을 순회하는 반복 (Iterable 기반)
1) 개념
- for...of는 iterable 객체에서 값의 시퀀스를 꺼내며 반복한다. (Array, String, Map, Set, NodeList 등)
for (const value of iterable) {
// ...
}
2) 동작 원리(정확한 규칙)
- 루프 시작 시 iterable의 [Symbol.iterator]()를 호출해 iterator를 얻고, 그 iterator의 next()를 반복 호출하여 값을 가져온다.
- break 등으로 루프가 조기 종료되면, iterator의 return()이 호출되어 정리(cleanup)가 수행될 수 있다.
const arr = ["a", "b", "c"];
for (const x of arr) console.log(x); // a, b, c
3) “객체(Object)”는 왜 바로 for...of가 안 되나?
- 일반적인 plain object는 기본적으로 iterable이 아니다(즉, [Symbol.iterator]()가 없음).
따라서 객체를 순회하려면 보통 다음 패턴을 쓴다. - 객체를 for...of로 순회(권장 패턴)
const obj = { a: 1, b: 2 };
for (const [k, v] of Object.entries(obj)) {
console.log(k, v);
}
Object.entries()는 객체의 own enumerable string-keyed key-value 쌍을 배열로 반환한다.
2.6 for...in: “키(key)”를 순회하는 반복 (Enumerable property 기반)
1) 개념
for...in은 객체의 enumerable string 프로퍼티 이름(키)을 순회한다. 이때 상속(prototype chain)으로 물려받은 enumerable 프로퍼티도 포함하며, Symbol 키는 무시한다.
for (const key in object) {
// key는 문자열 프로퍼티 이름
}
2) 동작 특성(꼭 알아야 하는 포인트)
- own + inherited 모두 순회한다(프로토타입 체인 포함).
- 순회 대상은 enumerable 프로퍼티로 제한된다. enumerable 여부는 Object.defineProperty의 enumerable 플래그로 제어되고, for...in/Object.keys는 보통 enumerable만 본다.
- 순회 순서는 “정의되어 있고 구현 간 일관”하다고 MDN이 설명하지만, 실제로는 순서 의존 로직을 만드는 것은 피하는 편이 안전하다.
3) 배열에 for...in을 쓰면 왜 위험한가?
- 배열 인덱스는 결국 “프로퍼티 키(문자열)”이며, for...in은 배열의 정수 인덱스 외에 비정수 키(사용자가 추가한 프로퍼티)나 상속된 enumerable 키까지 포함할 수 있다. 그래서 배열은 보통 for...of 또는 일반 for를 권장한다.
const arr = [10, 20];
arr.custom = 999;
for (const k in arr) {
console.log(k); // "0", "1", "custom" ... (환경/상황에 따라 더 있을 수도)
}
4) “자기 자신(own) 프로퍼티만” 돌고 싶으면?
- for...in 대신 아래를 사용다.
- Object.keys(obj) : own + enumerable string keys
- Object.getOwnPropertyNames(obj) : own + (enumerable 포함) string keys
3. 함수
3.1 함수란?
함수(Function)는 작업 단위를 정하고, 일부를 분리해서 관리하기 위한 코드 묶음이다.
또한 자바스크립트에서 함수는 값처럼 취급되는(First-class) 객체라서 변수에 담고, 다른 함수에 인자로 넘기고, 심지어 함수를 반환할 수도 있다.
3.2 함수 정의(선언) 방법 4가지
1) 함수 선언문(Function Declaration)
function 함수이름(x) {
return x * x;
}
- 선언문은 호이스팅(hoisting)의 영향을 받아 같은 스코프에서는 “위에 있는 것처럼” 취급되어, 선언보다 먼저 호출 가능한 형태가 된다.
2) 함수 표현식(Function Expression)
var fn = function (x) {
return x * x;
};
- 함수가 “값”이므로 변수에 대입할 수 있다.
- 호이스팅은 함수 선언문처럼 동작하지 않는다.(변수 선언만 끌어올려지고, 함수 대입은 실행 시점에 일어남)
3) Function 생성자(Function Constructor)
var fn = new Function("x", "return x * x");
- 문자열로 런타임에 함수를 만들 수 있지만, eval()과 유사한 보안/성능 이슈가 있고, 생성된 함수는 전역 스코프에서만 실행된다는 제약이 있다.
- 실무에서는 특별한 이유가 없으면 권장되지 않는다.
4) 화살표 함수(Arrow Function)
var fn = (x) => x * x;
- 함수 표현식의 간결한 대안이다. 다만 의미적 차이/제한이 있으며(예: 자체 this 바인딩이 다름, arguments 없음, 생성자(new)로 사용 불가 등), 문맥에 맞게 선택해야 한다.
3.3 함수 호출(실행)
정의한 함수는 이름 뒤에 괄호 ()를 붙여 호출한다.
function sayHello() {
console.log("안녕하세요?");
}
sayHello(); // 함수 호출
계산/출력 로직도 함수로 분리하면 재사용과 관리가 쉬워진다.
function calcAndPrintNumbers() {
var numberOne = 10;
var numberTwo = 20;
var result = numberOne + numberTwo;
console.log(numberOne + " + " + numberTwo + " = " + result);
}
calcAndPrintNumbers();
3.4 매개변수(parameter)와 인자(argument)
1) 기본 문법
function myFunction(name, area) {
document.write("안녕하세요. " + name + " 입니다." + "<br>");
document.write("사는곳은 " + area + " 입니다." + "<br><br>");
}
myFunction("tony", "캘리포니아");
- name, area는 매개변수(parameter)
- 호출할 때 넘기는 "tony", "캘리포니아"는 인자(argument)
참고: document.write()는 학습용으로는 괜찮지만, 실제 서비스에서는 DOM 조작/렌더링 방식으로 대체하는 편이 일반적이다.
2) 인자 개수가 달라도 “일단 실행”은 된다
- 더 많이 넘기면: 초과 인자는 기본 매개변수로는 직접 받지 않지만(필요하면 rest/arguments로 받음) 실행은 된다.
- 덜 넘기면: 전달되지 않은 매개변수는 기본적으로 undefined가 된다.
- 수동 처리:
function getCalcNumbers(numberOne, numberTwo) {
if (numberTwo === undefined) {
numberTwo = 0;
}
return numberOne + numberTwo;
}
getCalcNumbers(10); // numberTwo는 undefined → 0으로 보정
- 현대 JS 권장(기본값 매개변수):
function getCalcNumbers(numberOne, numberTwo = 0) {
return numberOne + numberTwo;
}
- 기본값 매개변수는 “값이 없거나 undefined가 들어오면 기본값을 사용”한다.
- 추가로, 가변 인자를 다루려면 rest parameter를 사용한다.
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
3.5 return 문과 반환값
return은 함수 실행을 즉시 종료하고, 호출한 쪽에 값을 돌려준다.
function getCalcNumbers() {
var numberOne = 10;
var numberTwo = 20;
var result = numberOne + numberTwo;
return result; // 여기서 함수 종료 + 값 반환
}
var calcResult = getCalcNumbers();
console.log(calcResult);
- return 뒤에 값이 없거나 return을 만나지 않고 끝나면 반환값은 undefined다.
3.6 변수의 영역(Scope)과 함수
핵심은 "함수 기반 스코프"다. 특히 var는 함수 스코프(function-scoped)라서, 블록(if {}) 안에서 선언해도 함수 전체에서 보일 수 있다.
function foo() {
var count = 0;
if (true) {
var bar = 10; // var는 블록 스코프가 아님
}
console.log(count, bar); // 둘 다 접근 가능
}
또한 전역/지역 변수의 범위는 다음처럼 정리된다.
var name = "global";
function foo() {
var area = "local";
console.log(name); // 전역 접근 가능
return area;
}
foo();
console.log(area); // ReferenceError (지역변수는 함수 밖에서 접근 불가)
추가 정리(현대 JS): let, const는 블록 스코프(block-scoped)다. 즉 {} 안에서 선언하면 그 블록 밖에서 접근할 수 없다.
3.7 중첩 함수(Nested Function)
중첩 함수는 특정 함수 내부에 선언된 함수로, 내부 구현을 감추거나(캡슐화), 상위 함수의 데이터를 함께 쓰는 구조를 만들 때 활용한다.
function getCalcNumbers(numberOne, numberTwo) {
function calcPlus(a, b) {
return a + b;
}
return calcPlus(numberOne, numberTwo);
}
getCalcNumbers(10, 60);
- 외부에서는 내부 함수를 직접 호출할 수 없다. (접근 범위가 함수 내부로 제한됨)
주의(정확한 관점): “if/while 안에는 중첩함수를 작성할 수 없다”고 생각할 수 있는데, 문법적으로는 가능하다. 다만 if {} 같은 블록 내부의 함수 선언은 모드/환경에 따라 동작이 복잡하거나 일관되지 않을 수 있어 권장되지 않는 편이고, 조건부로 함수를 만들려면 보통 함수 표현식을 사용한다.
3.8 재귀 함수(Recursive Function)
재귀는 함수가 자기 자신을 다시 호출하는 방식이다. 주의할 점은 반드시 종료 조건(기저 사례, base case)을 둬야 한다는 것이다.
function countUp(n) {
if (n === 10) return; // 종료 조건
console.log(n);
countUp(n + 1); // 자기 호출
}
countUp(0);
- 재귀는 스택을 사용하므로, 종료 조건이 없으면 무한 호출로 문제가 발생할 수 있다.
3.9 콜백 함수(Callback Function)
콜백은 다른 함수에 “인자로 전달되는 함수”이며, 외부 함수 내부에서 “특정 시점”에 호출된다.
function getCalcNumbers(callback) {
var result = callback(10, 60);
return result;
}
var callbackFunction = function (numberOne, numberTwo) {
return numberOne + numberTwo;
};
var result = getCalcNumbers(callbackFunction);
- 이벤트 처리, 비동기 처리, 배열 메서드(map/filter) 등에서 콜백이 매우 자주 쓰인다.
3.10 함수를 리턴하는 함수(고차 함수의 한 형태)
자바스크립트는 함수가 값이므로 함수를 반환할 수 있고, 이런 함수는 대표적으로 고차 함수(higher-order function)로 분류된다.
function getCalcNumbers(numberOne, numberTwo) {
return function () { // 값을 반환하는 게 아니라 함수를 반환
return numberOne + numberTwo;
};
}
var calc = getCalcNumbers(10, 80);
var result = calc(); // 반환된 함수를 실행
이 패턴으로 다음이 가능다.
- 호출 시점에 "맞춤 동작"을 가진 함수를 만들어 반환(팩토리)
- 한 번 만들어 둔 함수를 재사용
- 경우에 따라 자기 자신을 재정의하는 함수(상태에 따라 다음 호출 동작 변경)도 구현 가능
3.11 클로저(closure)
클로저는 "함수 + 그 함수가 선언될 당시의 렉시컬 환경(outer scope)"의 결합으로, 함수가 바깥 스코프의 변수를 계속 참조할 수 있게 한다.
이 특성 때문에 private 변수(외부에서 직접 접근 불가한 상태)를 만들 때 자주 사용된다.
function counter() {
var count = 0; // private 변수
return function () {
return ++count; // 외부에서 count를 직접 못 건드리고, 함수로만 증가
};
}
var countFn = counter();
countFn(); // 1
countFn(); // 2
countFn(); // 3
'웹 (Web) > 프런트엔드' 카테고리의 다른 글
| JavaScript에서의 HTTP API 통신 XHR / Fetch / Axios / AJAX 간단 비교 (0) | 2026.01.20 |
|---|---|
| Javascript의 객체에 대하여 (0) | 2026.01.20 |
| React를 위한 JavaScript 기초 훑어보기 (2/2) (1) | 2026.01.09 |
| React를 위한 JavaScript 기초 훑어보기 (1/2) (0) | 2026.01.06 |
| 주요 html 엔티티 정리 (1) | 2026.01.06 |