자바스크립트에는 여덟 가지 자료형이 있다. 이 중 일곱 개는 오직 하나의 데이터만을 담을 수 있는 ‘원시형'이다.
객체형은 원시형과 달리 다양한 데이터를 담을 수 있다. 키로 구분된 데이터 집합이나 복잡한 개체를 저장할 수 있다. 객체는 자바스크립트 거의 모든 면에 녹아있는 개념이므로 자바스크립트를 잘 다루려면 객체를 잘 이해하고 있어야 한다.
객체는 중괄호를 이용해 만들 수 있다. 중괄호 안에는 키:값
쌍으로 구성된 프로퍼티를 여러 개 넣을 수 있는데, 키로는 문자형, 값으로는 모든 자료형이 허용된다. 프로퍼티 키는 프로퍼티 이름이라고 부르기도 한다.
서랍장을 상상하면 객체를 이해하기 쉽다. 서랍장 안에 든 파일을 프로퍼티, 파일 각각에 붙어있는 이름표는 객체의 키라고 생각하면 된다. 복잡한 서랍장 안에서 이름표가 있으면 원하는 파일을 쉽게 찾을 수 있듯이, 객체에서는 키를 사용해 프로퍼티를 쉽게 찾을 수 있다. 추가나 삭제도 쉽게 할 수 있다.
빈 객체를 만드는 방법은 두 개가 있다.
let user = new Object(); // '객체 생성자' 문법
let user = {}; // '객체 리터럴' 문법
중괄호를 이용해 객체를 선언하는 것을 객체 리터럴이라 하며, 객체 선언 시 주로 선언하는 방법이다.
중괄호 안에는 키:값
쌍으로 구성된 프로퍼티가 들어간다.
let user = { // 객체
name: "John", // 키: "name", 값: "John"
age: 30 // 키: "age", 값: 30
};
프로퍼티 키는 프로퍼티 이름, 식별자 라고도 한다.
객체 user에는 두 개의 프로퍼티가 있다.
서랍장(객체) 안에 파일 두개(프로퍼티 두 개)가 담겨 있는데, 각 파일에 “name”, “age” 라는 이름표가 붙어있다고 생각하면 쉽다.
점 표기법으로 프로퍼티 값을 읽을 수도 있다.
// 프로퍼티 값 얻기
alert( user.name ); // John
alert( user.age ); // 30
서랍장에 파일을 넣고 뺄 수 있듯, 프로퍼티를 추가하고 삭제할 수 있다.
user.isAdmin = true;
delete 연산자를 사용해 프로퍼티를 삭제할 수 있다.
delete user.age;
프로퍼티 이름에 공백이 포함된 경우 따옴표로 묶어 준다.
let user = {
name: "John",
age: 30,
"likes birds": true // 복수의 단어는 따옴표로 묶어야 합니다.
};
마지막 프로퍼티에 쉼표를 붙여 끝낼 수 있는데, 이런 쉼표를 ‘trailing(길게 늘어지는)’ 혹은 ‘hanging(매달리는)’ 쉼표라고 한다. 끝에 쉼표를 붙이면 모든 프로퍼티가 유사한 형태를 보이므로 프로퍼티를 추가, 삭제, 이동하기 쉬워진다.
여러 단어를 조합해 프로퍼티 키를 만든 경우, 점 표기법을 사용해 값을 읽어올 수 없다.
// 불가능한 방법
user.likes birds = true
점 표기법은 유효한 변수 식별자인 경우, 즉 공백이 없고 숫자로 시작하지 않으며 $, _ 외의 특수문자가 없어야 사용 가능하다.
점 표기법을 사용할 수 없는 경우 ‘대괄호 표기법' 을 사용하면 된다.
let user = {};
// set
user["likes birds"] = true;
// get
alert(user["likes birds"]); // true
// delete
delete user["likes birds"];
다음과 같이 변수를 키로 사용하는 것도 가능해진다.
let key = "likes birds";
user[key] = true;
변수를 사용해서 사용자 입력값 변경 등에 따라 key 를 바꿔줄 수 있다.
let user = {
name: "John",
age: 30
};
let key = prompt("사용자의 어떤 정보를 얻고 싶으신가요?", "name");
// 변수로 접근
alert( user[key] );
// 프롬프트 창에 "name"을 입력한 경우 John 리턴
// "age" 입력한 경우 30 리턴
점 표기법으로는 이러한 방식을 이용할 수 없다.
let user = {
name: "John",
age: 30
};
let key = "name";
alert( user.key ) // undefined
객체를 만들 때 객체 리터럴 안의 프로퍼티 키가 대괄호로 둘러싸여 있는 경우, 이를 계산된 프로퍼티라고 한다.
let fruit = prompt("어떤 과일을 구매하시겠습니까?", "apple");
let bag = {
[fruit]: 5, // 변수 fruit에서 프로퍼티 이름을 동적으로 받아 옵니다.
};
alert( bag.apple ); // fruit에 "apple"이 할당되었다면, 5가 출력됩니다.
위 예시에서 bag 객체의 프로퍼티 키 [fruit] 는 프로퍼티 키를 변수 fruit 에서 가져오겠다는 것을 의미한다.
사용자가 apple 을 입력하면 bag 에는 {apple:5} 가 할당된다.
대괄호 안에는 복잡한 표현식이 올 수도 있다.
let fruit = 'apple';
let bag = {
[fruit + 'Computers']: 5 // bag.appleComputers = 5
};
대괄호 표현법은 프로퍼티 키와 값의 제약을 없애주기 때문에 점 표기법보다 강력하지만, 작성하기가 번거롭다는 단점이 있다. 따라서 프로퍼티 이름이 확정된 상황이고 단순한 이름이라면 점 표기법을 사용하다가, 복잡한 상황이 발생하면 대괄호 표기법으로 바꾸는 경우가 많다.
실무에서는 프로퍼티 값을 기존 변수에서 받아와 사용하는 경우가 종종 있다.
function makeUser(name, age) {
return {
name: name,
age: age,
// ...등등
};
}
let user = makeUser("John", 30);
alert(user.name); // John
위 코드에서 프로퍼티들은 이름과 변수의 값이 변수의 이름과 동일하다. 이런 경우 프로퍼티 값 단축 구문을 사용하면 코드를 짧게 줄일 수 있다.
function makeUser(name, age) {
return {
name, // name: name 과 같음
age, // age: age 와 같음
// ...
};
}
한 객체에서 일반 프로퍼티와 단축 프로퍼티를 같이 사용할 수도 있다.
let user = {
name, // name: name 과 같음
age: 30
};
객체 프로퍼티 이름으로 for, let, return 등 예약어를 사용할 수 있다.
문자형이나 심볼형이 아닌 값은 문자열로 자동 형 변환된다.
let obj = {
0: "test" // "0": "test"와 동일합니다.
};
// 숫자 0은 문자열 "0"으로 변환되기 때문에 두 얼럿 창은 같은 프로퍼티에 접근
alert( obj["0"] ); // test
alert( obj[0] ); // test (동일한 프로퍼티)
단, 특별 대우를 받는 이름이 하나 있는데, __proto__
이다.
let obj = {};
obj.__proto__ = 5; // 숫자를 할당합니다.
alert(obj.__proto__); // [object Object] - 숫자를 할당했지만 값은 객체가 된다.
__proto__
의 프로퍼티 값으로 원시값 5를 할당했지만 무시되었다. 더 자세한 내용은 추후에 알아보기로 하자!
자바스크립트 객체의 중요 특징 중 하나는 존재하지 않는 프로퍼티에 접근하려 해도 에러가 발생하지 않고, undefined 를 반환한다는 것이다.
let user = {};
alert( user.noSuchProperty === undefined );
// true는 '해당 프로퍼티가 존재하지 않음'을 의미
in 연산자를 사용해 프로퍼티 존재 여부를 확인할 수 있다.
"key" in object
let user = { name: "John", age: 30 };
alert( "age" in user ); // user.age가 존재하므로 true
alert( "blabla" in user ); // user.blabla는 존재하지 않기 때문에 false
프로퍼티 이름은 보통 따옴표로 감싼 문자열이다. 따옴표를 생략하면 엉뚱한 변수가 조사 대상이 된다.
let user = { age: 30 };
let key = "age";
alert( key in user );
// true, 변수 key에 저장된 값("age")을 사용해 프로퍼티 존재 여부를 확인
in 연산자는 프로퍼티 값으로 undefined 가 할당되어있는 경우 존재여부를 정확히 판별할 수 있다.
let obj = {
test: undefined
};
alert( obj.test );
// test라는 이름의 프로퍼티가 존재하지만 값이 `undefined`이므로, 얼럿 창엔 undefined가 출력
alert( "test" in obj );
// true 가 출력된다. `in`을 사용하면 프로퍼티 유무를 제대로 확인 가능
한편, 값을 알 수 없거나 비어있다는 것을 나타낼 때에는 undefined 대신 null 을 주로 사용한다.
for in 반복문을 사용하면 객체의 모든 키를 순회할 수 있다.
for (key in object) {
// 각 프로퍼티 키(key)를 이용하여 본문(body)을 실행합니다.
}
아래 예시를 사용하면 객체의 모든 프로퍼티가 출력된다.
for (let key in user) {
// 키
alert( key ); // name, age, isAdmin
// 키에 해당하는 값
alert( user[key] ); // John, 30, true
}
객체는 특별한 방식으로 정렬된다.
정수 프로퍼티는 자동으로 정렬되고, 그 외의 프로퍼티는 객체에 추가한 순서 그대로 정렬된다.
let codes = {
"49": "독일",
"41": "스위스",
"44": "영국",
// ..,
"1": "미국"
};
for (let code in codes) {
alert(code); // 1, 41, 44, 49
}
49, 41, 44, 1 의 순서대로 객체에 추가했기 때문에 49가 가장 먼저 출력될 것 같지만, 1이 첫 번째로 출력된다!
키가 정수여서 프로퍼티가 자동 정렬되었기 때문이다.
💡 정수 프로퍼티란?자동 정렬을 피하려면 다음과 같이 + 를 붙여주면 된다.
let codes = {
"+49": "독일",
"+41": "스위스",
"+44": "영국",
// ..,
"+1": "미국"
};
for (let code in codes) {
alert( +code ); // 49, 41, 44, 1 - 숫자형으로 다시 바꾸어 출력하므로 + 가 사라진다.
}