객체는 키와 값의 쌍으로 이루어진 프로퍼티의 컬렉션입니다.
키에는 문자열과 심볼만 쓸 수 있으며, 값에는 JS에 존재하는 어떠한 값이든 쓸 수 있습니다.
값을 할당할 수 있는 모든 곳에 객체 리터럴을 기재하여 객체를 생성할 수 있습니다.
const a = { k: 3 };
foo({ bar: "value" });
new 연산자로 생성자 함수를 호출하면, 생성자의 prototype 프로퍼티를 프로토타입으로 하는 객체를 생성할 수 있습니다.
※ 모든 객체는 Object 의 instance 이므로 new Object()
로 객체를 생성할 수 있으나 이는 권장되지 않습니다.
이러한 경우에는 빈 객체 리터럴({}
)을 사용하는 것이 좋습니다.
_Q. 왜 권장되지 않는가?
매개변수로 받은 proto를 프로토타입으로 하는 객체를 생성합니다.
const a = { k: 3 };
const b = Object.create(a);
const C = class {
constructor(name) {
this.name = name;
}
};
const c = new C("test");
객체 안에 존재하는 일반적인 프로퍼티입니다. 키와 값, Boolean 으로 된 속성(attributes)으로 구성됩니다.
attribute name | value domain | description | default value |
---|---|---|---|
[[value]] | 모든 타입 | 키에 대응되는 값 | undefined |
[[writable]] | Boolean | 값([[value]] ) 변경 가능 여부 | false |
[[enumerable]] | Boolean | 열거 가능 여부 | false |
[[configurable]] | Boolean | false인 경우, 1. 프로퍼티를 삭제할 수 없으며 2. 접근자 프로퍼티로 변경할 수 없으며, 3. 속성 또한 변경할 수 없습니다 (※ [[value]] 는 변경할 수 있습니다) | false |
키와 접근자 함수(accessor function), Boolean 으로 된 속성으로 구성됩니다. 접근자 함수는 특수한 메서드로서, 메서드를 호출하는 형태가 프로퍼티를 읽거나 쓰는 것처럼 보입니다.
데이터 프로퍼티는 실제 저장 공간을 차지하나 접근자 프로퍼티는 실제 저장 공간을 차지하지 않습니다. 일종의 가상 프로퍼티라고 생각해도 좋습니다.
attribute name | value domain | description | default value |
---|---|---|---|
[[get]] | Function | Undefined | 프로퍼티에 접근할 때마다 호출됩니다 | undefined |
[[set]] | Function | Undefined | 프로퍼티에 값을 할당하려고 할때마다 호출됩니다 [[set]] 은 반드시 [[get]] 의 반환값에 영향을 줄 필요는 없습니다 | undefined |
[[enumerable]] | Boolean | 열거 가능 여부 | false |
[[configurable]] | Boolean | false 인 경우, 1. 프로퍼티를 삭제할 수 없으며 2. 데이터 프로퍼티로 변경할 수 없으며 3. 속성을 변경할 수 없습니다 | false |
ECMAScript 언어 명세에만 존재하는 프로퍼티로서 명세에서는 내부 프로퍼티 키 이름을 대괄호로 감싸서 표현합니다.
자바스크립트에서 내부 프로퍼티에 직접적으로 접근할 방법은 없지만, 간접적으로는 가능합니다. 예를 들어 [[Prototype]]에는 객체의 프로토타입이 들어 있으며, 직접적으로 접근할 수는 없지만 Object.getPrototypeOf()
메서드를 이용하여 간접적으로 접근할 수 있습니다.
프로퍼티 키는 반드시 유효한 식별자여야 합니다.
객체.프로퍼티_키
으로 프로퍼티에 접근가능합니다.
// 예시
const obj = {};
obj.foo = 2;
프로퍼티 키는 유효한 식별자가 아니어도 가능합니다.
대괄호 안에는 어떠한 표현식도 올 수 있으며, 표현식을 평가한 값은 문자열 또는 심볼이어야 합니다.
객체[표현식]
의 형태로 프로퍼티에 접근가능합니다.
// 예시
const obj = {};
obj.foo = 2;
const propertyKey = "foo";
console.log(obj[propertyKey]); // 2
[[prototype]]
에 기록해둡니다. 객체는 자신의 프로토타입 객체의 프로퍼티를 상속합니다. [[prototype]]
을 묶는 체인을 프로토타입 체인이라고 합니다. 객체의 프로퍼티에 접근할 때 JS는 그 객체에서 프로퍼티 검색을 시작해서, 프로퍼티가 존재하지 않으면 프로토타입 객체로, 프로토타입 객체에도 프로퍼티가 존재하지 않으면 프로토타입 객체의 프로토타입 객체로 검색을 확장합니다. 즉, 프로토타입 체인을 거슬러서 검색합니다. 프로토타입 체인을 이용해 마치 단일 객체인 것처럼 행동합니다. 프로토타입 체인 내 정의된 메서드를 호출시, 메서드 내 this
는 메서드를 소유한 객체(홈 객체)가 아니라 실제 메서드를 호출한 객체입니다. own property
)이며, 프로토타입 체인을 통해 접근가능한 프로퍼티는 상속한 프로퍼티(inherited property
)라고 부릅니다. delete
연산자 사용), delete
연산자는 true
를 반환하지만 상속한 프로퍼티는 삭제되지 않습니다. 열거가능 Only | 열거가능여부 무관 | |
---|---|---|
고유 Only | Object.keys(obj) | Object.getOwnPropertyNames(obj) |
고유 상속 Both | for-in loop | N/A(*1) |
const getAllPropertyNames = (obj) => {
const allPropertyNames = [];
while(obj) {
allPropertyNames.push(...Object.getOwnPropertyNames(obj));
obj = Object.getPrototypeOf(obj);
}
return ret;
}
Q. Reflect.ownkeys()
찾아보기
obj.hasOwnProperty(key)
hasOwnProperty
프로퍼티는 Object.prototype
에 정의된 메서드로서(상속된 프로퍼티이므로), obj.hasOwnProperty
라는 고유 프로퍼티에 의해 이 메서드가 오버라이딩되었을 수 있습니다. 따라서 Object.prototype
을 통해 직접 호출하는 것이 더 낫습니다.
Object.prototype.hasOwnProperty.call(obj, key)
또는 {}.hasOwnProperty.call(obj, key)
in
연산자프로퍼티 키의 열거여부와는 무관합니다.
※ 점 연산자 또는 대괄호 연산자를 이용해서 프로퍼티 키의 존재여부를 확인하는 것은 좋은 방법이 아닙니다. 프로퍼티의 키가 존재하나 그 값이 undefined
인 경우와 키가 존재하지 않아 undefined
가 반환되는 경우를 구분할 수 없기 때문입니다.
const c = {}
Object.defineProperty(c, "d", {value: undefined})
console.log("d" in c) // true
console.log(Boolean(c.d)) // false
프로퍼티 서술자는 프로퍼티 속성의 조작 및 구체화를 설명할 때 사용됩니다.
Object.getOwnPropertyDescriptor(obj, propKey)
Object.getOwnPropertyDescriptors(obj)
Object.defineProperty(obj, propKey, propDesc)
Object.defineProperties(obj, propDescObj)
Object.create(proto, propDescObj)
: proto
를 프로토타입으로 하는 객체를 만든 후, propDescObj
에 지정되어 있는 각 프로퍼티 서술자를 이용하여 프로퍼티를 만들거나 수정합니다.
=
를 통하여 프로퍼티를 할당한다는 건 기존 프로퍼티를 바꾼다는 뜻입니다.
새 고유 프로퍼티를 만들거나 기존 고유 프로퍼티의 속성을 업데이트합니다. 어느 쪽이든 프로토타입 체인은 완벽히 무시합니다.
상속 등은 클래스에서...