for-in 루프에서 열거 가능한 속성과 그렇지 않은 속성의 차이

조회수 685회
Object.prototype.contain = function(neddle) {
    for(var name in this){
        if(this[name] === neddle){
            return true;
        }
    }
    return false;
}
var o = {'name':'egoing', 'city':'seoul'};

for(var key in o){
    console.log(key);  
}

console에서

name
city
contain

이렇게 출력됩니다.

Object.prototype.contain = function~~ 으로 지정한 속성이 왜 생성한 객체를 출력시킬 때 출력되나요? 이미 Object.prototype에서 기본적으로 정의된 다른 속성들도 많은데 왜 직전에 정의한 contain만 하나 더 출력되는건지 궁금합니다.

1 답변

  • 좋아요

    3

    싫어요
    채택 취소하기

    기본적으로 사용자가 어떤 객체의 프로토타입을 확장하여 만든 속성값들은 그 확장된 객체가 인스턴스화 될 때 인스턴스의 enumerable entry에 포함됩니다. 이것은 JS의 프로토타입을 통한 상속 특성에 기인하는데 자세한 것은 프로토 타입 체인 설명을 살펴보세요.


    그렇다면 왜 Object.prototype안에 있는 다른 속성들은 for..in에서 나타나지 않을까요?

    이것은 해당 속성의 명세(property description)에 나타나 있습니다. 속성 명세를 얻기 위해서 Object.getOwnPropertyDescriptor()를 사용하는데요.

    Object를 상속받은 모든 객체가 가지고 있지만 for..in에 노출되지 않는 대표적인 속성인 toString을 살펴보면,

    var desc = Object.getOwnPropertyDescriptor(Object.prototype, 'toString');
    console.log(desc);
    /*
    { value: [Function: toString],
      writable: true,
      enumerable: false,
      configurable: true }
    */
    

    이런 결과가 나옵니다.

    여기에서 눈여겨 보아야 할 것은 enumerable: false 입니다.

    MDN 에 나와 있는 for..in의 설명을 보면

    The for...in statement iterates over all enumerable properties of an object that are keyed by strings (ignoring ones keyed by Symbols), including inherited enumerable properties.

    enumerable properties라는 말이 나옵니다. 이것은 열거 가능한 속성 이라는 의미인데, 이 열거와 관련된 제어 속성이 위에 봤던 enumerable 입니다.

    Object.toString 속성은 enumerable: false 이므로 열거 가능하지 않게 지정되어 있다고 보면 되며, 결국 for..in 반복문을 돌 때 skip되어 넘어가는 것입니다.

    이를 좀 더 쉽게 이해하기 위해 질문 코드를 바꿔볼게요. 프로토타입 확장된 contain 속성을 열거가 불가능하게 지정하여 for..in에서 노출시키지 않도록 하는 코드입니다. 속성 명세를 정의하기 위해서는 Object.defineProperty()를 사용합니다.

    Object.prototype.contain = function(neddle) {
        for(var name in this){
            if(this[name] === neddle){
                return true;
            }
        }
        return false;
    }
    
    // 여기서 'contain' property의 특성을 지정합니다.
    Object.defineProperty(Object.prototype, 'contain', {
        enumerable: false // 열거 불가능한 속성으로 지정
    });
    
    var o = {'name':'egoing', 'city':'seoul'};
    
    for(var key in o){
        console.log(key);  
    }
    /*
    name
    city
    */
    

    위 결과에서 추가로 알 수 있는 두 가지 사실은,

    1. 사용자가 확장하는 속성은 enumerable 명세가 기본으로(by default) true이다.
    2. 표준 내장 객체의 기본 속성 혹은 prototype 속성들은 대부분 enumerable: false로 지정되어 있다.

    eg.

    Object.getOwnPropertyDescriptor(Math, 'pow')
    // {writable: true, enumerable: false, configurable: true, value: ƒ}
    

    이 내용들은 직접 테스트 코드를 작성하여 확인해 보시면 될 것 같고요.

    이 현상과 관련하여 hasOwnProperty 함수의 동작 원리와 Symbol 속성에 대해 for..in이 어떻게 동작하는지를 추가로 알아두시면 대부분 이해했다고 볼 수 있겠네요.

    • 쓰리따봉 드립니다. 👍👍👍 편집요청빌런 2020.3.19 18:01
    • 정말 감사합니다~~~~ 이해가 잘 되네요 류건 2020.3.19 21:16

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)