LHJ

I'm a FE developer.

3.12 객체

19 Apr 2020 » js_lj

원시 타입은 단 하나의 값만 나타낼 수 있고 불변이지만,
이와 달리 객체는 여러 가지 값이나 복잡한 값을 나타낼 수 있으며, 변할 수도 있습니다.
객체의 본질은 컨테이너입니다.
컨테이너의 내용물은 시간이 지나면서 바뀔 수 있지만, 내용물이 바뀐다고 컨테이너가 바뀌는 건 아닙니다.
즉, 여전히 같은 객체입니다.
객체에도 중괄호, 즉 {과 }를 사용하는 리터럴 문법이 있습니다.
중괄호는 한 쌍이므로 객체가 어디에서 시작하고 어디에서 끝나는지 나타낼 수 있습니다.
빈 객체로 시작해 봅시다.

const obj = {};

NOTE_ 객체 이름은 아무거나 써도 되지만, 일반적으로 user나 shoppingCart 처럼 의미를 알 수 있는 이름을 써야 합니다.
우리는 이제 막 객체에 대해 배우는 중이고 이 예제의 객체가 무언가를 나타내는 것은 아니니 그냥 obj라고 부릅니다.

객체의 컨텐츠는 프로퍼티(property) 또는 멤버(member) 라고 부릅니다.
프로퍼티는 이름(키)과 값으로 구성됩니다.
프로퍼티 이름은 반드시 문자열 또는 심볼이어야 하며, 값은 어떤 타입이든 상관없고 다른 객체여도 괜찮습니다.
obj에 color 프로퍼티를 추가합시다.

obj.color = "yellow";

프로퍼티 이름에 유효한 식별자를 써야 멤버 접근 연산자(member access operator)(.) 를 사용할 수 있습니다.
프로퍼티 이름에 유효한 식별자가 아닌 이름을 쓴다면 계산된 멤버 접근 연산자(computed member access operator)([]) 를 써야 합니다.
(이후 멤버 접근 연산자는 ‘점 연산자’, 계산된 멤버 접근 연산자는 ‘대괄호’라고 간단하게 표기하겠습니다.)
프로퍼티 이름이 유효한 식별자여도 대괄호로 접근할 수 있습니다.

obj["not an identifier"] = 3;
obj["not an identifier"];       // 3
obj["color"];                   // "yellow"

심볼 프로퍼티에 접근할 때도 대괄호를 사용합니다.

const SIZE = Symbol();
obj[SIZE] = 8;
obj[SIZE]; // 8

이제 obj에는 “color” (유효한 식별자 문자열), “not an identifier” (유효한 식별자가 아닌 문자열), SIZE(심볼) 세 가지 프로퍼티가 있습니다.

NOTE_ 자바스크립트 콘솔에서 이 예제를 실행해 보면 콘솔에서는 SIZE를 obj의 프로퍼티로 나열하지 않는 걸 볼 수 있습니다(console.log(obj)).

이미지

obj[SIZE]를 입력해 보면 SIZE가 obj의 프로퍼티인 것을 확인할 수 있지만, 심볼 프로퍼티는 다르게 처리되며 기본적으로는 표시되지 않습니다.
또한, 이 프로퍼티의 키는 SIZE 심볼이며 문자열 “SIZE”가 아닙니다.
obj.SIZE = 0을 입력한 후 obj[SIZE]와 obj.SIZE(또는 obj[“SIZE”])를 입력해 보면 이 사실을 확인할 수 있습니다.(점 연산자는 문자열 프로퍼티에 대해 항상 동작합니다.)

이 단계에서 잠시 멈추고 원시 값과 객체의 차이에 대해 되새겨 봅시다.
이 섹션에서 우리는 변수 obj에 저장된 객체를 저장했지만, obj는 항상 같은 객체를 가리키고 있었습니다.
obj에 저장한 것이 문자열이나 숫자, 기타 다른 원시 값이었다면 수정할 때마다 다른 값을 가리켰을 겁니다.
달리 말해 obj는 계속 같은 객체를 가리키고, 바뀐 것은 객체의 프로퍼티입니다.
(역주_ 참조형과 원시형의 차이 및 동작에 관해서는 3장의 마지막 부분에 따로 설명합니다.)

obj는 빈 객체로 만들었지만, 객체 리터럴 문법에서는 객체를 만드는 동시에 프로퍼티를 만들 수 있습니다.
중괄호 안에서 각 프로퍼티를 쉼표로 구분하고, 프로퍼티 이름과 값은 콜론으로 구분합니다.

const sam1 = {
	name: 'Sam',
	age: 4,
};

const sam2 = { name: 'Sam', age: 4 }; // 한 줄로 선언했습니다.

const sam3 = {
	name: 'Sam',
	classification: {    // 프로퍼티 값도 객체가 될 수 있습니다.
		kingdom: 'Anamalis',
		phylum: 'Chordata',
		class: 'Mamalia',
		order: 'Carnivoria',
		family: 'Felidae',
		subfamily: 'Felinae',
		genus: 'Felis',
		species: 'catus',
	}
}

이 예제에서는 객체 리터럴 문법에 따라 세 가지 객체를 만들었습니다.
sam1과 sam2의 프로퍼티는 똑같지만, 둘은 서로 다른 객체입니다.
원시 값과는 반대입니다(값이 숫자 3인 두 변수는 같은 원시 값을 가리킵니다).
sam3의 classification 프로퍼티는 그 자체가 객체입니다.
sam3의 family에 접근하는 방법은 여러가지 입니다.
여기서는 큰 따옴표를 썼지만, 작은 따옴표나 백틱을 써도 됩니다.

sam3.classification.family;         // "Felidae"
sam3["classification"].family;      // "Felidae"
sam3.classification["family"];      // "Felidae"
sam3["classification"]["family"];   // "Felidae"

객체에 함수를 담을 수도 있습니다.
함수에 대해서는 6장에서 자세히 설명합니다.
지금은 함수가 일종의 부속 프로그램이라고 생각하면 됩니다.
sam3에 함수를 추가할 때는 다음과 같이 합니다.

sam3.speak = function() { return "Meow!"; };

이제 함수 뒤에 괄호를 붙여 함수를 호출할 수 있습니다.

sam3.speak(); // "Meow!"

마지막으로, 객체에서 프로퍼티를 제거할 때는 delete 연산자를 사용합니다.

delete sam3.classification;     // classification 트리 전체가 삭제됐습니다.
delete sam3.speak;              // speak 함수가 삭제됐습니다.

객체지향 프로그래밍(OOP)에 익숙하다면 자바스크립트 객체를 OOP에 어떻게 사용하는지 궁금할 겁니다.
지금은 객체를 범용 컨테이너라고 생각하십시오.
OOP에 대해서는 9장에서 설명합니다.