본문 바로가기

CS/모던 자바스크립트 Deep Dive

19장 프로토타입(3)

반응형

2023년 4월 7일 209p~312p

 

19장 프로토타입

19.11 직접상속

19.11.1 Object.create에 의한 직접 상속

Object.create 메서드는 명시적으로 프로토타입을 지정하여 새로운 객체를 생성한다. Object.create 메서드도 다른 객체 생성 방식과 마찬가지로 추상 연산 OrdinaryObjectCreate를 호출한다.

Object.create의 첫번째 매개변수에는 생성할 객체의 프로토타입으로 지정할 객체를 전달하고, 두번째 매개변수에는 생성할 객체의 프로퍼티 키와 프로퍼티 디스크립터 객체로 이루어진 객체를 전달하며 이것은 옵션이다.

// 프로토타입이 null인 객체를 생성한다. 생성된 객체는 프로토타입 체인의 종점에 위치한다.
// obj -> null
let obj = Object.create(null);
console.log(Object.getPrototype(obj) === null); // true
// Object.prototype을 상속받지 못한다.
console.log(obj.toString()); // TypeError

// obj -> Object.prototype->null
// obj = {};
obj = Object.create(Object.prototype);
console.log(Object.getPrototype(obj) === Object.prototype); // true

// obj = {x:1}
// obj.x = 1과 동일
obj = Object.create(Object.prototype, {
	x: {value:1,writable:true,enumerable:true,configurable:true}
});

이 메서드의 장점은 다음과 같다.

  • new 연산자가 없이도 객체 생성
  • 프로토타입 지정하며 객체 생성
  • 객체 리터럴에 의해 생성된 객체도 상속 받을 수 있음

참고로 Object.prototype의 빌트인 메서드인 Object.prototype.hasOwnProperty, Object.prototype.isPrototypeOf 등의 모든 객체는 프로토타입 체인의 종점의 메서드이므로 모든 객체가 상속받아 호출할 수 있다. 하지만 ESLint에서는 Object.prototype의 빌트인 메서드를 객체가 직접 호출하는 것을 권장하지 않는다. Object.create 메서드를 통해 프로토타입 체인의 종점에 위치하는 객체를 생성할 수 없기 때문이다.

let obj = Object.create(null);
obj.a = 1;
console.log(Object.getPrototype(obj) === null); // true
// obj는 Object.prototype의 빌트인 메서드를 사용할 수 없다.
console.log(obj.hasOwnProperty('a')); // TypeError

따라서 다음과 같이 간접적으로 호출하는 것이 좋다.

let obj = Object.create(null);
obj.a = 1;
console.log(Object.getPrototype(obj) === null); // true
// Object.prototype의 빌트인 메서드는 객체로 직접 호출하지 않는다.
console.log(Object.prototype.hasOwnProperty.call(obj,'a')); // true

19.11.2 객체 리터럴 내부에서 __proto__에 의한 직접 상속

Object.create 메서드에 의한 직접 상속은 여러 장점이 있지만 번거롭다. ES6에서는 객체 리터럴 내부에서 __proto__ 접근자 프로퍼티를 사용하여 직접 상속을 구현할 수 있다.

const myProto = {x:10};

const obj = {
	y:20,
    // 객체를 직접 상속받는다.
    // obj->myProto->Object.prototype->null
    __proto__:myProto
};

consol.log(obj.x,obj,y); // 10 20 
console.log(Object.getPrototypeOf(obj) === myProto) // true

 

19.12 정적 프로퍼티/메서드

정적 프로퍼티/메서드는 생성자 함수로 인스턴스를 생성하지 않아도 참조/호출할 수 있는 프로퍼티/메서드를 말한다. 정적 프로퍼티/메서드는 생성자 함수 객체가 소유하며 인스턴스로 참조/호출 할 수 없다.

만약 인스턴스/프로토타입 메서드 내에서 this를 사용하지 않는다면 그 메서드는 정적 메서드로 변경할 수 있다. 인스턴스가 호출한 인스턴스/프로토타입 메서드 내에서 this는 인스턴스를 가리킨다. 메서드 내에서 인스턴스를 참조할 필요가 없다면 정적 메서드로 변경하여도 동작한다. 프로토타입 메서드를 호출하려면 인스턴스를 생성해야 하지만 정적 메서드는 인스턴스를 생성하지 않아도 호출할 수 있다.

 

19.13 프로퍼티 존재 확인

19.13.1 in 연산자

in 연산자는 객체 내에 특정 프로퍼티가 존재하는지 여부를 확인한다.

key in object

in 연산자는 확인 대상 객체의 프로퍼티 뿐 아니라 확인 대상 객체가 상속받은 모든 프로포타입의 프로퍼티를 확인하므로 주의가 필요하다.

in 연산자 대신 ES6에서 도입한 Reflect.has 메서드를 사용할 수도 있다. Reflect.has 메서드는 in 연산자와 동일하게 동작한다.

Reflect has(객체,프로퍼티)

19.13.2 Object.prototype.hasOwnProperty 메서드

Object.prototype.hasOwnProperty 메서드를 사용해도 객체에 특정 프로퍼티가 존재하는지 확인할 수 있다.

객체.hasOwnProperty(프로퍼티)

인수로 전달 받은 프로퍼티 키가 객체 고유의 프로퍼티인 경우에만 true를 반환하고, 상속받은 프로토타입의 프로퍼티 키인 경우 false를 반환한다.

 

19.14 프로퍼티 열거

19.14.1 for...in 문

객체의 모든 프로퍼티를 순회하여 열거하려면 for...in문을 사용한다.

for(변수선언문 in 객체){}

for in 문은 in 연산자처럼 순회 대상 객체의 프로퍼티뿐 아니라 상속받은 프로토타입의 프로퍼티까지 열거한다. 

const person = {
	name : "lee",
    address : "seoul"
};

// in 연산자는 객체가 상속받은 모든 프로토타입의 프로퍼티를 확인한다.
console.log('toString' in person) // true

for(const key in person) {
    console.log(key person[key]);
};

// 하지만 toString과 같은 Object.prototype의 프로퍼티가 열겨되지는 않는다.

// name lee
// address seoul

toString 메서드는 열겨할 수 없도록 정의되어 있는 프로퍼티이기 때문이다. 다시 말해 Object.prototype.toString 프로퍼티의 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false이기 때문이다. 프로퍼티 어트리뷰트 [[Enumerable]]은 프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 갖는다.

즉, for in문은 객체의 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중에서 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 true인 프로퍼티를 순회하며 열거한다. 상속받은 프로퍼티를 제외하고 객체 자신의 프로퍼티만 열거하려면 Object.prototype.hasOwnProperty 메서드를 사용하여 객체 자신의 프로퍼티인지 확인해야 한다.

const person = {
    name : "lee",
    address : "seoul"
    __proto__ : {age:20}
};

for(const key in person) {
    // 객체가 자신의 프로퍼티인지 확인한다.
    if(!person.hasOwnProperty(key)) continue;
    console.log(key person[key]);
};

// name lee
// address seoul

프로퍼티를 열거할 때 순서가 보장되지는 않지만 대부분의 모던 브라우저는 순서를 보장하고 숫자(사실은 문자열)인 프로퍼티 키에 대해서는 정렬을 실시한다.

배열은 for in 문 보다는 for, for of, forEach를 사용하기를 권장한다. 사실 배열도 객체이므로 프로퍼티와 상속받은 프로퍼티가 포함될 수 있다.

19.14.2 Object.keys/values/entries 메서드

객체 자신의 고유 프로퍼티만 열거하기 위해서는 for in 보다 Object.keys/values/entries 메서드를 사용하는 것을 권장한다. 

Object.keys : 객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환

Object.keys(객체)

Object.values : 객체 자신의 열거 가능한 프로퍼티 값을 배열로 반환

Object.values(객체)

Object.entries : 객체 자신의 열거 가능한 프로퍼티 키와 값의 쌍을 배열에 담아 반환

Object.entries(객체)
반응형