for … in 문으로 객체의 프로퍼티를 나열하는 방법은 이미 봤습니다.
이제 프로토타입 상속에 대해 알게 됐으니 객체의 프로퍼티를 나열할 때 hasOwnProperty 가 어떤 의미가 있는지 완전히 이해할 수 있습니다.
객체 obj와 프로퍼티 x에서, obj.hasOwnProperty(x) 는 obj에 프로퍼티 x가 있다면 true를 반환하며, 프로퍼티 x가 obj에 정의되지 않았거나 프로토타입 체인에만 정의되어있다면 false를 반환합니다.
ES6 클래스를 설계 의도대로 사용한다면 데이터 프로퍼티는 항상 프로토타입 체인이 아니라 인스턴스에 정의해야 합니다.
하지만 프로퍼티를 프로토타입에 정의하지 못하도록 강제하는 장치는 없으므로 확실히 확인하려면 항상 hasOwnProperty를 사용하는 편이 좋습니다.
예제를 보십시오.
class Super {
constructor () {
this.name = "Super";
this.isSuper = true;
}
}
// 유효하지만, 권장하지는 않습니다.
Super.prototype.sneaky = 'not recommended';
class Sub extends Super {
constructor() {
super();
this.name = 'Sub';
this.isSub = true;
}
}
const obj = new Sub();
for (let p in obj) {
console.log(`${p}: ${obj[p]}` + (obj.hasOwnProperty(p) ? '' : ' (inherited)'));
}
이 프로그램을 실행한 결과는 다음과 같습니다.
name: Sub
isSuper: true
isSub: true
sneaky: not recommended! (inherited)
name, isSuper, isSub 프로퍼티는 모두 프로토타입 체인이 아니라 인스턴스에 정의됏습니다(슈퍼클래스 생성자에서 선언한 프로퍼티는 서브클래스 인스턴스에도 정의됩니다).
반면 sneaky 프로퍼티는 슈퍼클래스의 프로토타입에 직접 정의했습니다.
Object.keys를 사용하면 프로토타입 체인에 정의된 프로퍼티를 나열하는 문제를 피할 수 있습니다.
왜냐면 Object.keys는 상속안된 프로퍼티 중 문자열로 표현할 수 있는 것만 나열하므로..(맞나?)