객체, 프로토타입 in JS

동동·2021년 6월 25일
1
post-thumbnail

객체란 무엇인가요?

객체는 키와 값의 쌍으로 이루어진 프로퍼티의 컬렉션입니다.

키에는 문자열과 심볼만 쓸 수 있으며, 값에는 JS에 존재하는 어떠한 값이든 쓸 수 있습니다.

객체를 생성하는 방법

1. 객체 리터럴: {}

값을 할당할 수 있는 모든 곳에 객체 리터럴을 기재하여 객체를 생성할 수 있습니다.

const a = { k: 3 };

foo({ bar: "value" });

2. 생성자

new 연산자로 생성자 함수를 호출하면, 생성자의 prototype 프로퍼티를 프로토타입으로 하는 객체를 생성할 수 있습니다.

※ 모든 객체는 Object 의 instance 이므로 new Object() 로 객체를 생성할 수 있으나 이는 권장되지 않습니다.

이러한 경우에는 빈 객체 리터럴({})을 사용하는 것이 좋습니다.

_Q. 왜 권장되지 않는가?

3. Object.create(proto)

매개변수로 받은 proto를 프로토타입으로 하는 객체를 생성합니다.


const a = { k: 3 };

const b = Object.create(a);

4. class

const C = class {
  constructor(name) {
    this.name = name;
  }
};

const c = new C("test");

프로퍼티의 종류

데이터 프로퍼티(data property)

객체 안에 존재하는 일반적인 프로퍼티입니다. 키와 값, Boolean 으로 된 속성(attributes)으로 구성됩니다.

attribute namevalue domaindescriptiondefault value
[[value]]모든 타입키에 대응되는 값undefined
[[writable]]Boolean값([[value]]) 변경 가능 여부false
[[enumerable]]Boolean열거 가능 여부false
[[configurable]]Booleanfalse인 경우,
1. 프로퍼티를 삭제할 수 없으며
2. 접근자 프로퍼티로 변경할 수 없으며,
3. 속성 또한 변경할 수 없습니다
(※ [[value]]는 변경할 수 있습니다)
false

접근자 프로퍼티(accessor property)

키와 접근자 함수(accessor function), Boolean 으로 된 속성으로 구성됩니다. 접근자 함수는 특수한 메서드로서, 메서드를 호출하는 형태가 프로퍼티를 읽거나 쓰는 것처럼 보입니다.

데이터 프로퍼티는 실제 저장 공간을 차지하나 접근자 프로퍼티는 실제 저장 공간을 차지하지 않습니다. 일종의 가상 프로퍼티라고 생각해도 좋습니다.

attribute namevalue domaindescriptiondefault value
[[get]]Function | Undefined프로퍼티에 접근할 때마다 호출됩니다undefined
[[set]]Function | Undefined프로퍼티에 값을 할당하려고
할때마다 호출됩니다
[[set]]은 반드시 [[get]]
반환값에 영향을 줄 필요는 없습니다
undefined
[[enumerable]]Boolean열거 가능 여부false
[[configurable]]Booleanfalse 인 경우,
1. 프로퍼티를 삭제할 수 없으며
2. 데이터 프로퍼티로 변경할 수 없으며
3. 속성을 변경할 수 없습니다
false

내부 프로퍼티

ECMAScript 언어 명세에만 존재하는 프로퍼티로서 명세에서는 내부 프로퍼티 키 이름을 대괄호로 감싸서 표현합니다.
자바스크립트에서 내부 프로퍼티에 직접적으로 접근할 방법은 없지만, 간접적으로는 가능합니다. 예를 들어 [[Prototype]]에는 객체의 프로토타입이 들어 있으며, 직접적으로 접근할 수는 없지만 Object.getPrototypeOf() 메서드를 이용하여 간접적으로 접근할 수 있습니다.

프로퍼티 접근방법

점 연산자: Dot Notation

프로퍼티 키는 반드시 유효한 식별자여야 합니다.
객체.프로퍼티_키 으로 프로퍼티에 접근가능합니다.

// 예시
const obj = {};

obj.foo = 2;

대괄호 연산자: Bracket Notation

프로퍼티 키는 유효한 식별자가 아니어도 가능합니다.
대괄호 안에는 어떠한 표현식도 올 수 있으며, 표현식을 평가한 값은 문자열 또는 심볼이어야 합니다.

객체[표현식]의 형태로 프로퍼티에 접근가능합니다.

// 예시

const obj = {};

obj.foo = 2;

const propertyKey = "foo";

console.log(obj[propertyKey]); // 2

프로토타입, 프로토타입 체인

  1. 모든 객체는 다른 객체와 프로토타입 관계를 맺고 있습니다. 객체는 자신의 프로토타입 객체를 [[prototype]]에 기록해둡니다. 객체는 자신의 프로토타입 객체의 프로퍼티를 상속합니다.
  2. 객체와 [[prototype]]을 묶는 체인을 프로토타입 체인이라고 합니다. 객체의 프로퍼티에 접근할 때 JS는 그 객체에서 프로퍼티 검색을 시작해서, 프로퍼티가 존재하지 않으면 프로토타입 객체로, 프로토타입 객체에도 프로퍼티가 존재하지 않으면 프로토타입 객체의 프로토타입 객체로 검색을 확장합니다. 즉, 프로토타입 체인을 거슬러서 검색합니다. 프로토타입 체인을 이용해 마치 단일 객체인 것처럼 행동합니다. 프로토타입 체인 내 정의된 메서드를 호출시, 메서드 내 this는 메서드를 소유한 객체(홈 객체)가 아니라 실제 메서드를 호출한 객체입니다.
  3. 객체에 직접 정의된 프로퍼티는 고유 프로퍼티(own property)이며, 프로토타입 체인을 통해 접근가능한 프로퍼티는 상속한 프로퍼티(inherited property)라고 부릅니다.
  4. 프로토타입 체인 전체를 검색하는 건 프로퍼티를 읽을 때 뿐입니다. 프로퍼티를 설정하거나 삭제할 때는 상속을 무시하고, 고유 프로퍼티에만 영향을 미칩니다. 즉, 프로토타입 체인 내의 프로퍼티와 동명의 프로퍼티를 설정하면 프로토타입 체인 내의 프로퍼티는 변경시키지 않고, 고유 프로퍼티를 추가합니다. 또한 프로퍼티를 삭제할 때(delete 연산자 사용), delete 연산자는 true를 반환하지만 상속한 프로퍼티는 삭제되지 않습니다.

프로퍼티 키 순회 방법

열거가능 Only열거가능여부 무관
고유 OnlyObject.keys(obj)Object.getOwnPropertyNames(obj)
고유 상속 Bothfor-in loopN/A(*1)

*1) 고유 상속 Both / 열거가능여부 무관 프로퍼티 키 순회 함수

const getAllPropertyNames = (obj) => {
  const allPropertyNames = [];
  
  while(obj) {
    allPropertyNames.push(...Object.getOwnPropertyNames(obj));
    
    obj = Object.getPrototypeOf(obj); 
  }
  
  return ret;
}

Q. Reflect.ownkeys() 찾아보기

프로퍼티 키 존재여부 확인 방법

1. 고유 only: obj.hasOwnProperty(key)

hasOwnProperty 프로퍼티는 Object.prototype 에 정의된 메서드로서(상속된 프로퍼티이므로), obj.hasOwnProperty라는 고유 프로퍼티에 의해 이 메서드가 오버라이딩되었을 수 있습니다. 따라서 Object.prototype을 통해 직접 호출하는 것이 더 낫습니다.

Object.prototype.hasOwnProperty.call(obj, key)
또는 {}.hasOwnProperty.call(obj, key)

2. 고유 상속 Both: 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에 지정되어 있는 각 프로퍼티 서술자를 이용하여 프로퍼티를 만들거나 수정합니다.

프로퍼티 할당 vs 프로퍼티 정의

프로퍼티 할당:

=를 통하여 프로퍼티를 할당한다는 건 기존 프로퍼티를 바꾼다는 뜻입니다.

프로퍼티 정의

고유 프로퍼티를 만들거나 기존 고유 프로퍼티의 속성을 업데이트합니다. 어느 쪽이든 프로토타입 체인은 완벽히 무시합니다.

Object.preventExtensions, Object.seal, Object.freeze

상속 등은 클래스에서...

Reference

profile
작은 실패, 빠른 피드백, 다시 시도

0개의 댓글