오늘은 JavaScript의 객체(Object) 와 배열(Array), 그리고 다양한 배열 helper method 를 학습했다. 파이썬과 비교하며 정리해보자.
원시 자료형 (불변, 값을 복사해서 저장)
number : 정수/실수 구분이 없음string : 문자열, 템플릿 리터럴 `${변수명}` 사용 가능boolean : true / falsenull : 의도적으로 값이 없음undefined : 값이 할당되지 않음참조 자료형 (가변, 주소를 복사)
array, object, function, class// 함수 선언식 (호이스팅 O)
function add(a, b) {
return a + b
}
// 함수 표현식 (호이스팅 X)
const add = function (a, b) {
return a + b
}
// 화살표 함수
const add = (a, b) => a + b
증감 연산자 위치에 따른 차이
// 후위 연산: 할당 후 증가
let a = 1
let b = a++ // b = 1, a = 2
// 전위 연산: 증가 후 할당
let c = 1
let d = ++c // d = 2, c = 2
동등 vs 일치
| 연산자 | 설명 | 예시 |
|---|---|---|
== | 동등 연산자, 암묵적 형변환 후 비교 | 1 == '1' → true |
=== | 일치 연산자, 타입까지 비교 | 1 === '1' → false |
for ... in : 객체의 키에 접근for ... of : 객체의 값에 접근키로 구분된 데이터 집합을 저장하는 자료형 (파이썬의
dictionary와 유사)
{} 를 이용해 작성key 는 문자형만 허용 (따옴표 생략 가능 → 알아서 문자 취급)value 는 모든 자료형 허용속성이 객체에 존재하는지 확인
⚠️
==대신===,in대신hasOwnProperty()를 쓰는 것이 더 안전!
in연산자는 프로토타입 체인을 따라 상속된 속성까지 확인하므로 의도치 않은true가 나올 수 있음.
thisthis 키워드를 사용해 객체 자신의 속성/메서드에 접근 가능
const person = {
name: 'Alice',
greeting: function () {
return `Hello my name is ${this.name}`
},
}
console.log(person.greeting()) // Hello my name is Alice
this 특징this 를 암묵적으로 전달 받음this 가 동적으로 결정됨self, Java의 this 는 선언 시점에 결정되지만, JavaScript는 호출 시점에 결정| 구분 | 내용 |
|---|---|
| ✅ 장점 | 함수를 하나만 만들어 여러 객체가 공유하며 각자 자신의 데이터로 동작 가능 |
| ⚠️ 단점 | 유연함이 실수로 이어질 수 있음 |
Key-Value 형태로 이루어진 자료 표기법
| 변환 방향 | JavaScript | Python |
|---|---|---|
| Object → JSON | JSON.stringify() | json.dumps() |
| JSON → Object | JSON.parse() | json.loads() |
const name = 'Alice'
const age = 30
// 단축 전
// const user = { name: name, age: age }
// 단축 후
const user = { name, age }
// 단축 전
// const myObj = {
// myFunc: function () { return 'Hello' }
// }
// 단축 후
const myObj = {
myFunc() {
return 'Hello'
}
}
const product = prompt('물건 이름을 입력해주세요')
const prefix = 'my'
const suffix = 'property'
const bag = {
[product]: 5,
[prefix + suffix]: 'value'
}
console.log(bag) // { 연필: 5, myproperty: 'value' }
const userInfo = {
firstName: 'Alice',
userId: 'alice123',
email: 'alice123@gmail.com'
}
// 한 번에 여러 변수에 할당
const { firstName, userId, email } = userInfo
console.log(firstName, userId, email)
// 함수 매개변수에서 활용
function printInfo({ firstName, email }) {
console.log(`이름: ${firstName}, 이메일: ${email}`)
}
printInfo(userInfo)
const obj = { b: 2, c: 3, d: 4 }
const newObj = { ...obj, a: 1, e: 5 }
console.log(newObj) // { a: 1, b: 2, c: 3, d: 4, e: 5 }
const profile = { name: 'Alice', age: 30 }
Object.keys(profile) // ['name', 'age']
Object.values(profile) // ['Alice', 30]
Object.entries(profile) // [['name', 'Alice'], ['age', 30]]
?.)속성이 없는 중첩 객체에 접근할 때 에러 없이 안전하게 접근하는 방법.
참조 대상이null또는undefined라면 평가를 멈추고undefined를 반환.
const userData = {
name: 'Alice',
greeting: function () {
return 'hello'
}
}
// 예전 방식 (&& 로 체크)
console.log(userData.address && userData.address.street) // undefined
// 변수 옵셔널 체이닝
console.log(userData.address.street) // ❌ TypeError
console.log(userData.address?.street) // ✅ undefined
// 함수 옵셔널 체이닝
console.log(userData.nonMethod()) // ❌ TypeError
console.log(userData.nonMethod?.()) // ✅ undefined
1. 남용 금지 - 존재하지 않아도 괜찮은 대상에만 사용
// ❌ Bad - user는 반드시 있어야 하는 값
userData?.address?.street
// ✅ Good - address만 선택적
userData.address?.street
2. Optional chaining 앞의 변수는 반드시 선언되어 있어야 함
✨ 장점 : 예외처리 코드가 짧아진다.
순서가 있는 데이터 집합을 저장하는 자료구조
| 메서드 | 동작 | Python 대응 |
|---|---|---|
push() | 배열 끝에 요소 추가 | list.append() |
pop() | 배열 끝 요소 제거 후 반환 | list.pop() |
unshift() | 배열 앞에 요소 추가 | deque.appendleft() |
shift() | 배열 앞 요소 제거 후 반환 | deque.popleft() |
함수의 인자로 전달되어 특정 시점에 호출되는 함수.
→ 함수의 실행 권한을 다른 함수에 위임하는 것
const numbers = [1, 2, 3, 4]
// 콜백 함수 1: 각 요소를 두 배로
const double = (number) => number * 2
// 콜백 함수 2: 각 요소를 제곱
const square = (number) => number * number
console.log(numbers.map(double)) // [2, 4, 6, 8]
console.log(numbers.map(square)) // [1, 4, 9, 16]
console.log('a')
setTimeout(() => {
console.log('b')
}, 3000)
console.log('c')
// 출력 순서: a → c → b
forEach - 순회매개변수
item (필수) : 현재 처리 중인 요소index (선택) : 현재 요소의 인덱스array (선택) : 메서드를 호출한 배열 본체const names = ['Alice', 'Bella', 'Cathy']
// 일반 함수 표기
names.forEach(function (name) {
console.log(name)
})
// 화살표 함수 표기
names.forEach((name) => {
console.log(name)
})
// 활용
names.forEach((name, index, array) => {
console.log(`${name} / ${index} / ${array}`)
})
map - 변형원본 배열의 요소를 1:1 매핑하여 새로운 배열을 반환 (원본 불변)
const persons = [
{ name: 'Alice', age: 20 },
{ name: 'Bella', age: 21 }
]
// for...of 방식
let result1 = []
for (const person of persons) {
result1.push(person.name)
}
// map 방식
const result2 = persons.map((person) => person.name)
console.log(result2) // ['Alice', 'Bella']
💡 Python
map과의 차이
- Python :
map(함수, 리스트)→list()로 형변환 필요- JavaScript :
배열.map(함수)→ 즉시 배열 반환
filter - 선별콜백 함수의 반환값이 true 인 요소만 모아서 새로운 배열을 반환
⚠️
return값이 반드시 Boolean (true/false) 이어야 한다!
const numbers = [1, 2, 3, 4, 5]
// 짝수만 걸러내기
const evens = numbers.filter((num) => num % 2 === 0)
console.log(evens) // [2, 4]
활용 예시
const products = [
{ id: 1, name: 'Cucumber', type: 'vegetable' },
{ id: 2, name: 'Banana', type: 'fruit' },
{ id: 3, name: 'Carrot', type: 'vegetable' },
{ id: 4, name: 'Apple', type: 'fruit' },
]
// 1. 'fruit' 만 필터링 (카테고리 기능)
const fruits = products.filter((product) => product.type === 'fruit')
// 2. id가 3인 상품 '삭제' (사실은 3번 빼고 남기기)
const deletedList = products.filter((product) => product.id !== 3)
| 메서드 | 역할 |
|---|---|
find | 콜백의 반환값이 true 인 첫 번째 요소 반환 |
some | 하나라도 통과하면 true (즉시 순회 중지), 모두 실패하면 false |
every | 모두 통과하면 true, 하나라도 실패하면 false (즉시 순회 중지) |
const parts = ['어깨', '무릎']
const lyrics = ['머리', ...parts, '발']
console.log(lyrics) // ['머리', '어깨', '무릎', '발']
| 방식 | 특징 | 사용 상황 |
|---|---|---|
기본 for 문 | 인덱스(i)로 요소에 접근break, continue 사용 가능 | 인덱스 제어가 복잡하게 필요할 때 |
for ... of | 배열의 요소(값)에 바로 접근break, continue 사용 가능 | 중간에 멈추거나 건너뛰어야 할 때 |
forEach() | 간결, 가독성 ↑break, continue 사용 불가 | 처음부터 끝까지 순회할 때 (모던 웹 개발 핵심 패턴) |
reduce - 집계 (심화)arr.reduce((acc, cur, index, array) => {
return nextAccValue
}, initialValue)
파라미터
acc (Accumulator, 누적값) : 이전 콜백이 return 한 값 (눈덩이)cur (Current, 현재값) : 지금 처리 중인 요소 (새로 붙일 눈)initialValue (초기값) : 눈덩이를 굴리기 시작할 때의 크기const numbers = [1, 2, 3, 4, 5]
const sum = numbers.reduce((acc, cur) => {
console.log(`누적값(acc): ${acc}, 현재값(cur): ${cur}`)
return acc + cur
}, 0)
console.log('최종 결과:', sum) // 15
const names = ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob', 'Alice']
const nameCounts = names.reduce((countMap, name) => {
countMap[name] = (countMap[name] || 0) + 1
return countMap
}, {})
console.log(nameCounts)
// { Alice: 3, Bob: 2, Charlie: 1 }
map + filter 를 reduce로 한 번에// 체이닝 방식
// const result = nums.filter(n => n % 2 === 0).map(n => n * 2)
const nums = [1, 2, 3, 4, 5]
const result = nums.reduce((newArray, current) => {
if (current % 2 === 0) { // filter 역할
newArray.push(current * 2) // map 역할
}
return newArray
}, [])
console.log(result) // [4, 8]
this 의 동작 방식이 Python의 self 와 달라서 헷갈렸지만, "호출되는 방식" 에 따라 결정된다는 핵심을 잡았다.map, filter, reduce 같은 배열 메서드는 처음엔 어렵지만 익숙해지면 코드가 훨씬 깔끔해진다. 특히 reduce 는 만능...💭 "배열도 객체다" —
new키워드로 새 객체를 만들 수 있다는 점도 잊지 말자.