학습 자료

객체 — 이름으로 값을 찾는다


Article

배열은 값을 순서대로 담았다. 사용자 한 명을 배열로 표현하면 이렇게 된다.

const user = ["지원", 27, "seoul@example.com"];
console.log(user[1]);

user[1]이 나이라는 걸 어떻게 알까. 외워야 한다. 순서가 바뀌면 전부 깨진다.

값마다 이름을 붙여 담는 그릇이 객체다.

만들고 꺼낸다

중괄호 안에 키: 값 짝을 쉼표로 나열한다.

const user = {
  name: "지원",
  age: 27,
  email: "seoul@example.com",
};

console.log(user.name);
console.log(user["age"]);
console.log(user.phone);
콘솔
  • '지원'
  • 27
  • undefined

없는 키를 꺼내면 에러가 아니라 undefined다. 4강에서 본 "아직 값이 정해지지 않았다"는 그것이다.

꺼내는 방법이 둘이다.

점 표기법과 대괄호 표기법

user.name

점 표기법

  • 키를 직접 쓸 때
  • 짧고 읽기 좋다
  • 키에 공백이나 하이픈이 있으면 못 쓴다
  • 평소에는 이걸 쓴다

user[key]

대괄호 표기법

  • 키가 변수에 담겨 있을 때
  • 키를 동적으로 정할 때
  • user["my-key"] 처럼 특수문자도 가능
  • 안에 들어가는 건 문자열이다

차이가 결정적인 순간은 이때다.

const user = { name: "지원", age: 27 };
const key = "name";

console.log(user[key]);
console.log(user.key);
콘솔
  • '지원'
  • undefined

user.key"key"라는 이름의 키를 찾는다. 그런 건 없다. 변수에 담긴 키로 접근하려면 반드시 대괄호를 쓴다.

추가하고 바꾸고 지운다

const user = { name: "지원" };

user.age = 27;
user["city"] = "서울";
user.name = "민수";
delete user.city;

console.log(user);
콘솔
  • { name: '민수', age: 27 }

없는 키에 값을 넣으면 추가되고, 있는 키에 넣으면 덮어쓴다. delete로 지운다.

const인데 다 된다. 15강의 배열과 똑같은 이유다.

const 객체

이름과 객체의 연결만 잠겼다. 객체 내용은 열려 있다

user
0x3c4d
{ name: '민수', age: 27 }

user = {} 라고 다른 객체를 넣는 건 막히지만, 안을 고치는 건 막지 않는다.

중첩과 옵셔널 체이닝

객체 안에 객체를, 배열을 담을 수 있다.

const user = {
  name: "지원",
  address: {
    city: "서울",
    zip: "04524",
  },
  tags: ["개발", "디자인"],
};

console.log(user.address.city);
console.log(user.tags[0]);
콘솔
  • '서울'
  • '개발'

문제는 중간이 비어 있을 때다.

const user = { name: "지원" };

console.log(user.address);
console.log(user.address.city);
콘솔
  • undefined
  • TypeError: Cannot read properties of undefined (reading 'city')

user.addressundefined인데 거기서 .city를 또 꺼내려니 터진다. undefined에서는 아무것도 꺼낼 수 없다.

이걸 막는 문법이 옵셔널 체이닝 ?.이다.

const user = { name: "지원" };

console.log(user.address?.city);
console.log(user.address?.city ?? "주소 없음");
콘솔
  • undefined
  • '주소 없음'

?.는 앞이 null이나 undefined거기서 멈추고 undefined를 준다. 에러 대신 조용히 물러난다.

6강의 ??와 짝을 이룬다. 없으면 기본값을 채운다.

메서드 — 객체 안의 함수

값 자리에 함수를 넣으면 메서드라 부른다. 11강에서 함수가 값이라고 했으니 당연한 일이다.

const counter = {
  count: 0,
  increase() {
    this.count++;
    return this.count;
  },
};

console.log(counter.increase());
console.log(counter.increase());
콘솔
  • 1
  • 2

this가 등장했다. 여기서는 "이 메서드를 호출한 객체", 즉 counter를 가리킨다. 정확한 규칙은 19강에서 다룬다.

키를 순회한다

객체는 배열이 아니라서 map이나 filter를 쓸 수 없다. 대신 키·값을 배열로 뽑아내는 함수가 있다.

const scores = { 수학: 90, 영어: 85, 과학: 95 };

console.log(Object.keys(scores));
console.log(Object.values(scores));
console.log(Object.entries(scores));
콘솔
  • ['수학', '영어', '과학']
  • [90, 85, 95]
  • [['수학', 90], ['영어', 85], ['과학', 95]]

배열로 바뀌었으니 16강의 메서드를 전부 쓸 수 있다.

const scores = { 수학: 90, 영어: 85, 과학: 95 };

const total = Object.values(scores).reduce((acc, n) => acc + n, 0);
console.log(total);

const high = Object.entries(scores).filter(([, score]) => score >= 90);
console.log(high);
콘솔
  • 270
  • [['수학', 90], ['과학', 95]]

([, score])는 다음 강의에서 배울 구조분해다. 첫 요소를 건너뛰고 두 번째만 꺼냈다.

for...in으로 키를 직접 돌 수도 있다. 9강에서 예고한 그것이다.

const scores = { 수학: 90, 영어: 85 };

for (const subject in scores) {
  console.log(subject, scores[subject]);
}
콘솔
  • 수학 90
  • 영어 85

값이 있는지 확인한다

const user = { name: "지원", age: 0, nickname: undefined };

console.log(user.age ? "있음" : "없음");
console.log("age" in user);
console.log(Object.hasOwn(user, "nickname"));
console.log(user.nickname !== undefined);
콘솔
  • '없음'
  • true
  • true
  • false

첫 줄이 함정이다. age0이라 falsy로 판정됐다. 7강의 그 함정이다. 키가 있는지 묻고 싶다면 in이나 Object.hasOwn()을 쓴다.

객체도 참조 타입이다

4강에서 본 이야기를 다시 확인한다.

const a = { count: 1 };
const b = a;
b.count = 99;

console.log(a.count);
console.log(a === b);

const c = { count: 1 };
console.log(a === c);
콘솔
  • 99
  • true
  • false

b를 바꿨는데 a가 바뀌었다. 주소를 공유하기 때문이다.

마지막 줄이 중요하다. ac내용이 똑같은데도 다르다. ===는 내용이 아니라 같은 객체를 가리키는지를 본다.

무엇을 비교하는가
  • ===
    • 원시 타입숫자 · 문자열 · boolean
      • 값이 같으면 true
    • 객체 타입객체 · 배열 · 함수
      • 같은 주소를 가리켜야 true

정리하면

  • 객체는 키와 값의 짝을 담는다. 없는 키를 꺼내면 undefined다.
  • 키가 변수에 담겨 있다면 반드시 대괄호로 접근한다. user.key"key"를 찾는다.
  • const 객체도 내용은 바뀐다. 이름과 객체의 연결만 잠긴다.
  • 중간이 비었을 수 있다면 **?.**를 쓴다. ??와 짝지어 기본값을 채운다.
  • Object.keys · values · entries로 배열로 바꾸면 16강의 메서드를 쓸 수 있다.
  • 키의 존재는 in이나 Object.hasOwn()으로 확인한다. 값으로 판단하면 0에 걸린다.
  • 객체 비교는 내용이 아니라 주소를 본다.

다음 강의는 구조분해와 스프레드다. 객체와 배열을 훨씬 편하게 다루는 문법이고, 4강과 15강에서 미뤄둔 복사 문제를 여기서 끝낸다.

객체 — 이름으로 값을 찾는다 — 디코드랩(DCODELAB)