number와 string 같은 원시 데이터 타입 외에도, JavaScript에는 object라는 또 다른 중요한 데이터 타입이 있어요.
객체는 키-값 쌍의 모음이에요.
그래서 다른 언어에서 흔히 맵이나 딕셔너리라고 부르는 것으로 사용할 수 있어요.
다른 언어에서는 맵에 있는 모든 값이 같은 데이터 타입이어야 하는 경우가 많아요. JavaScript에서는 키의 타입만 제한되는데, 키는 문자열이어야 해요. 한 객체 안의 값들은 서로 다른 타입일 수 있어요. 숫자 같은 원시 데이터 타입일 수도 있고, 배열이나 다른 객체, 심지어 함수일 수도 있어요. 덕분에 객체는 매우 다재다능해서, JavaScript에서 객체 지향 프로그래밍(OOP)의 핵심 요소이기도 해요.
이제부터는 모음이나 맵으로서의 객체에 초점을 맞춰 볼게요. 객체의 다른 활용 사례는 다른 개념에서 다뤄요. 예를 들어 클래스를 참고해 보세요.
중괄호를 사용해 객체 리터럴을 만들어요. 항목(키-값 쌍)을 바로 포함할 수도 있어요. 이때 키를 먼저 쓰고, 콜론과 값을 이어서 써요.
const emptyObject = {};
const obj = {
favoriteNumber: 42,
greeting: 'Hello World',
useGreeting: true,
address: {
street: 'Trincomalee Highway',
city: 'Batticaloa',
},
fruits: ['melon', 'papaya'],
addNumbers: function (a, b) {
return a + b;
},
};
JavaScript에서는 마지막 항목 뒤의 후행 쉼표가 선택 사항이에요. 이것을 사용했을 때의 장점은 여기에서 읽어볼 수 있어요.
키가 문자열이어야 하는데 왜 따옴표로 감싸지 않았는지 궁금할 수 있어요. 이것은 축약 표기법이에요. 키가 JavaScript 식별자 작명 규칙을 따르면 따옴표를 생략할 수 있어요. 이름에 특수 문자가 들어간 키에는 일반적인 문자열 표기법을 사용해야 해요.
const obj = {
'1keyStartsWithNumber': '...',
'key/with/slashes': '...',
'key-with-dashes': '...',
'key with spaces': '...',
'#&()[]{}èä樹keyWithSpecialChars': '...',
};
객체를 선언할 때 이미 있는 변수를 추가하고 싶을 때가 많아요.
그러면 흔히 name: name 같은 키-값 쌍이 생겨요.
JavaScript에는 대신 name만 써도 되는 축약 표기법이 있어요.
객체 안에서 함수를 정의하는 축약 표기법도 있어요.
이때는 콜론과 function 키워드를 생략할 수 있어요.
const x = 1;
const y = 2;
const obj = {
x: x,
y: y,
calcSum: function (a, b) {
return a + b;
},
};
// Can be shortened to ...
const obj = {
x,
y,
calcSum(a, b) {
return a + b;
},
};
변수에 저장되어 있거나 표현식으로 계산된 키 이름으로 객체 리터럴을 만들고 싶다면, 그 변수나 표현식을 대괄호로 감싸야 해요.
const key = 'greeting';
const obj = {
[key]: 'hello world',
};
console.log(obj);
// => { greeting: 'hello world' }
주어진 키의 값을 가져오는 방법에는 점 표기법과 대괄호 표기법, 두 가지가 있어요.
const obj = { greeting: 'hello world' };
obj.greeting;
// => hello world
obj['greeting'];
// => hello world
// Bracket notation also works with variables.
const key = 'greeting';
obj[key];
// => hello world
축약형인 점 표기법에는 따옴표를 생략할 때와 같은 제약이 있어요. 키가 식별자 작명 규칙을 따를 때만 사용할 수 있어요.
객체에 없는 키를 가져오려고 하면 JavaScript는 undefined를 반환해요.
이에 대한 자세한 내용은 Null과 Undefined를 참고해요.
중첩된 객체에서 값을 가져오고 싶다면 키를 연이어 쓸 수 있어요.
const obj = {
address: {
street: 'Trincomalee Highway',
city: 'Batticaloa',
},
};
obj.address.city;
// => 'Batticaloa'
obj['address']['city'];
// => 'Batticaloa'
// Notations can also be mixed.
obj.address['city'];
// => 'Batticaloa'
할당 연산자 =를 사용해 값을 추가하거나 바꿀 수 있어요.
여기서도 점 표기법과 대괄호 표기법을 사용할 수 있어요.
const obj = { greeting: 'hello world' };
obj.greeting = 'Hi there!';
obj['greeting'] = 'Welcome.';
obj.newKey1 = 'new value 1';
obj['new key 2'] = 'new value 2';
const key = 'new key 3';
obj[key] = 'new value 3';
delete 키워드를 사용해 객체에서 키-값 쌍을 삭제할 수 있어요.
const obj = {
key1: 'value1',
key2: 'value2',
};
delete obj.key1;
delete obj['key2'];
없는 키에 대해서는 undefined가 반환되지만, 키를 undefined로 설정한다고 해서 항목이 제거되지는 않아요.
대신 항상 delete를 사용해요.
hasOwnProperty 메서드로 객체에 특정 키가 있는지 확인할 수 있어요.
const obj = { greeting: 'hello world' };
obj.hasOwnProperty('greeting');
// => true
obj.hasOwnProperty('age');
// => false
hasOwnProperty는 상속받은 키에 대해 false를 반환해요. 보통은 이게 원하는 동작이에요.
존재 여부를 확인할 때 상속받은 키도 포함하고 싶다면 대신 in 연산자를 사용할 수 있어요.
객체의 모든 키를 순회하는 특별한 for...in문이 있어요.
const obj = {
name: 'Ali',
age: 65,
};
for (let key in obj) {
console.log(key, obj[key]);
}
// name Ali
// age 65
for...in은 항상 키가 객체에 나타나거나 추가된 순서(삽입 순서)대로 방문하는 것처럼 보일 수 있어요.
그래도 순서가 삽입 순서와 달라지는 특별한 경우가 있으니, 이에 의존하면 안 돼요.
항상 순서가 임의적이라고 가정하고 코드를 작성해요.
믿을 만한 순서가 필요하다면 객체 대신 Map이나 중첩된 [배열][concept-arrays]을 사용해요.
for…in문에는 객체가 상속받은 키를 가질 때 생기는 함정이 있어요.
이런 키들도 for…in문이 방문해요.
보통은 이를 피하고 싶을 텐데, 그럴 때는 아래처럼 건너뛰어야 해요.
for (let key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
}
// ...
}
JavaScript에는 배열을 순회할 때 쓰는 for...of문도 있어요.
for...in과 for...of는 서로 바꿔 쓸 수 없다는 점이 중요해요. 자세한 내용은 MDN 문서를 참고해요.
내장 객체 Object는 주어진 객체의 모든 키, 값, 항목을 배열로 가져오는 도우미 메서드를 제공해요.
const obj = {
name: 'Ali',
age: 65,
};
Object.keys(obj);
// => [ 'name', 'age' ]
Object.values(obj);
// => [ 'Ali', 65 ]
Object.entries(obj);
// => [ [ 'name', 'Ali' ], [ 'age', 65 ] ]
JavaScript의 빈 객체가 완전히 비어 있지는 않다는 걸 눈치챘을 수도 있어요.
예를 들어 hasOwnProperty 메서드와 toString 같은 다른 메서드들을 가지고 있어요.
보통은 아무 문제도 일으키지 않지만, 완전히 빈 객체가 필요하다면 Object.create(null) 호출로 만들 수 있는 null 객체를 사용해요.