개체의 열거 할 수없는 상속 된 속성 이름을 가져올 수 있습니까?


99

자바 스크립트에는 우리가 얻고 자하는 것에 따라 객체의 속성을 얻는 몇 가지 방법이 있습니다.

1) Object.keys(), 객체의 모든 고유 한 열거 가능한 속성 인 ECMA5 메서드를 반환합니다.

2) for...in객체의 모든 열거 가능한 속성을 반환 하는 루프 (자체 속성인지 프로토 타입 체인에서 상속되었는지 여부에 관계없이)

3) Object.getOwnPropertyNames(obj)열거 가능 여부에 관계없이 개체의 모든 속성을 반환합니다.

또한 hasOwnProperty(prop)속성이 상속되었는지 또는 실제로 해당 객체에 속 propertyIsEnumerable(prop)하는지 확인하고 이름에서 알 수 있듯이 속성이 열거 가능한지 확인할 수있는 메서드도 있습니다 .

이 모든 옵션을 사용하면 열거 할 수없고 소유하지 않은 개체 속성 을 얻을 수있는 방법이 없습니다 . 이것이 제가 원하는 작업입니다. 이렇게 할 수있는 방법이 있습니까? 즉, 상속 된 열거 할 수없는 속성 목록을 어떻게 든 얻을 수 있습니까?

감사합니다.


4
귀하의 질문은 내가 물 으려고했던 질문에 답했습니다. 열거 할 수없는 속성을 검사하는 방법 (미리 정의 된 개체에서 사용할 수있는 항목을 탐색하기 위해). 마침내 getOwnPropertyNames를 찾았습니다! :-)
marcus 2011

1
@marcus :-) 그게 전부입니다!
dkugappi

답변:


115

때문에 getOwnPropertyNames당신이 아닌 열거 속성을 얻을 수 있습니다, 당신은을 사용하여 프로토 타입 체인을 걸어와 결합 할 수 있습니다.

function getAllProperties(obj){
    var allProps = []
      , curr = obj
    do{
        var props = Object.getOwnPropertyNames(curr)
        props.forEach(function(prop){
            if (allProps.indexOf(prop) === -1)
                allProps.push(prop)
        })
    }while(curr = Object.getPrototypeOf(curr))
    return allProps
}

Safari 5.1에서 테스트 한 결과

> getAllProperties([1,2,3])
["0", "1", "2", "length", "constructor", "push", "slice", "indexOf", "sort", "splice", "concat", "pop", "unshift", "shift", "join", "toString", "forEach", "reduceRight", "toLocaleString", "some", "map", "lastIndexOf", "reduce", "filter", "reverse", "every", "hasOwnProperty", "isPrototypeOf", "valueOf", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "propertyIsEnumerable", "__lookupSetter__"]

업데이트 : 코드를 약간 리팩터링했습니다 (공백 및 중괄호 추가, 함수 이름 개선) :

function getAllPropertyNames( obj ) {
    var props = [];

    do {
        Object.getOwnPropertyNames( obj ).forEach(function ( prop ) {
            if ( props.indexOf( prop ) === -1 ) {
                props.push( prop );
            }
        });
    } while ( obj = Object.getPrototypeOf( obj ) );

    return props;
}

1
덕분에 내가 이해하지 못하는 한 가지는 다음 while(curr = Object.getPrototypeOf(cure))과 같습니다. 조건문이 비교 연산자 대신 할당 연산자를 사용하므로 항상 true를 반환하지 않습니까? 아니면이 라인은 본질적으로 "curr"에 프로토 타입이 있는지 확인하는 것입니까?
dkugappi 2011

2
@AlexNabokov 결과가 거짓이면 거짓을 Object.getPrototypeOf(cure)반환 null하고 프로토 타입 체인의 맨 위에 반환 될 때 발생 합니다 . 원형 프로토 타입 체인이 없다고 가정합니다!
Domenic 2011

2
@Alex Function.prototype는 프로토 타입 링크가를 가리 키기 때문에 "루트"프로토 타입이 될 수 없습니다 Object.prototype. 이 함수 Object.getPrototypeOf( obj )는의 프로토 타입 체인에서 최상위 객체를 반환합니다 obj. obj끝 ( null가치)에 도달 할 때까지 프로토 타입 체인을 따라갈 수 있습니다 . 이 문제가 무엇인지 잘 모르겠습니다 ...
Šime Vidas

2
@Alex 아니요, 아닙니다 undefined. (반드시) 객체를 Object.getPrototypeOf(John)반환합니다 Boy.prototype-여기 참조 : jsfiddle.net/aeGLA/1 . 생성자가 있습니다 Boy입니다 하지 의 프로토 타입 체인에 John. 의 프로토 타입 체인은 John다음과 같습니다 Boy.prototype -> Object.prototype -> null..
Šime Vidas 2011

3
" Object.getPrototypeOf (obj)가 obj의 생성자의 프로토 타입을 반환 할 것이라고 생각했습니다 ."-예. 의 경우 John그의 생성자는 Boy이고의 prototype속성은 Boy입니다 Boy.prototype. 그래서 Object.getPrototypeOf(John)반환합니다 Boy.prototype.
Šime Vidas 2011

9

재귀를 사용하는 더 깨끗한 솔루션 :

function getAllPropertyNames (obj) {
    const proto     = Object.getPrototypeOf(obj);
    const inherited = (proto) ? getAllPropertyNames(proto) : [];
    return [...new Set(Object.getOwnPropertyNames(obj).concat(inherited))];
}

편집하다

더 일반적인 기능 :

function walkProtoChain (obj, callback) {
    const proto     = Object.getPrototypeOf(obj);
    const inherited = (proto) ? walkProtoChain(proto, callback) : [];
    return [...new Set(callback(obj).concat(inherited))];
}

function getOwnNonEnumPropertyNames (obj) {
    return Object.getOwnPropertyNames(obj)
        .filter(p => !obj.propertyIsEnumerable(p));
}

function getAllPropertyNames (obj) {
    return walkProtoChain(obj, Object.getOwnPropertyNames);
}

function getAllEnumPropertyNames (obj) {
    return walkProtoChain(obj, Object.keys);
}

function getAllNonEnumPropertyNames (obj) {
    return walkProtoChain(obj, getOwnNonEnumPropertyNames);
}

이 동일한 템플릿은 Object.getOwnPropertySymbols등을 사용하여 적용 할 수 있습니다 .


4

세트를 활용하면 다소 깨끗한 솔루션 인 IMO가 생성됩니다.

const own = Object.getOwnPropertyNames;
const proto = Object.getPrototypeOf;

function getAllPropertyNames(obj) {
    const props = new Set();
    do own(obj).forEach(p => props.add(p)); while (obj = proto(obj));
    return Array.from(props);
}

2

ES6의 간단한 반복 :

function getAllPropertyNames(obj) {
    let result = new Set();
    while (obj) {
        Object.getOwnPropertyNames(obj).forEach(p => result.add(p));
        obj = Object.getPrototypeOf(obj);
    }
    return [...result];
}

실행 예 :


1

일부 인스턴스에 대해 상속 된 모든 속성 또는 메서드를 얻으려면 다음과 같이 사용할 수 있습니다.

var BaseType = function () {
    this.baseAttribute = "base attribute";
    this.baseMethod = function() {
        return "base method";
    };
};

var SomeType = function() {
    BaseType();
    this.someAttribute = "some attribute";
    this.someMethod = function (){
        return "some method";
    };
};

SomeType.prototype = new BaseType();
SomeType.prototype.constructor = SomeType;

var instance = new SomeType();

Object.prototype.getInherited = function(){
    var props = []
    for (var name in this) {  
        if (!this.hasOwnProperty(name) && !(name == 'constructor' || name == 'getInherited')) {  
            props.push(name);
        }  
    }
    return props;
};

alert(instance.getInherited().join(","));

1
보다 사용하는 Object.getInherited것이 좋습니다 Object.prototype.getInherited. 그렇게하면 추한 !(name == 'getInherited')수표 가 필요하지 않습니다 . 또한 구현에서 props배열에 중복 속성이 포함될 수 있습니다. 마지막으로 constructor재산 을 무시하는 목적은 무엇 입니까?
Pauan 2013-07-17

object.getInherited는 언제 true가됩니까? 내가 상속 붙어로 질문 아래에서 확인하십시오 : stackoverflow.com/questions/31718345/...
라빈 드라 BABU에게

IMHO-이들은 Object가 아니라 Reflect에 속합니다. 또는-아니면 Object.keys (src, [settings]) 언어에서 기대할 수 있습니다. 여기서 선택적 설정은 숫자가 아닌 항목을 포함할지 여부를 지정할 수 있고, 상속을 포함할지 여부를 지정할 수 있습니다. , 기호를 포함하려면 최대 상속 깊이를 파십시오.
Radagast the Brown

어 ... Object.entries도 마찬가지입니다. 그래도 Object.values에 대해서는 확실하지 않습니다. ...잘. 왜 안돼.
Radagast the Brown

0

주제를 공부하면서 생각 해낸 해결책은 다음과 같습니다. obj개체 의 열거 할 수없는 모든 소유하지 않은 속성을 얻으려면getProperties(obj, "nonown", "nonenum");

function getProperties(obj, type, enumerability) {
/**
 * Return array of object properties
 * @param {String} type - Property type. Can be "own", "nonown" or "both"
 * @param {String} enumerability - Property enumerability. Can be "enum", 
 * "nonenum" or "both"
 * @returns {String|Array} Array of properties
 */
    var props = Object.create(null);  // Dictionary

    var firstIteration = true;

    do {
        var allProps = Object.getOwnPropertyNames(obj);
        var enumProps = Object.keys(obj);
        var nonenumProps = allProps.filter(x => !(new Set(enumProps)).has(x));

        enumProps.forEach(function(prop) {
            if (!(prop in props)) {
                props[prop] = { own: firstIteration, enum_: true };
            }           
        });

        nonenumProps.forEach(function(prop) {
            if (!(prop in props)) {
                props[prop] = { own: firstIteration, enum_: false };
            }           
        });

        firstIteration = false;
    } while (obj = Object.getPrototypeOf(obj));

    for (prop in props) {
        if (type == "own" && props[prop]["own"] == false) {
            delete props[prop];
            continue;
        }
        if (type == "nonown" && props[prop]["own"] == true) {
            delete props[prop];
            continue;
        }

        if (enumerability == "enum" && props[prop]["enum_"] == false) {
            delete props[prop];
            continue;
        }
        if (enumerability == "nonenum" && props[prop]["enum_"] == true) {
            delete props[prop];
        }
    }

    return Object.keys(props);
}

0
function getNonEnumerableNonOwnPropertyNames( obj ) {
    var oCurObjPrototype = Object.getPrototypeOf(obj);
    var arReturn = [];
    var arCurObjPropertyNames = [];
    var arCurNonEnumerable = [];
    while (oCurObjPrototype) {
        arCurObjPropertyNames = Object.getOwnPropertyNames(oCurObjPrototype);
        arCurNonEnumerable = arCurObjPropertyNames.filter(function(item, i, arr){
            return !oCurObjPrototype.propertyIsEnumerable(item);
        })
        Array.prototype.push.apply(arReturn,arCurNonEnumerable);
        oCurObjPrototype = Object.getPrototypeOf(oCurObjPrototype);
    }
    return arReturn;
}

사용 예 :

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

부모 개체의 열거 할 수없는 속성을 기록하려는 경우 ex. 기본적으로 es6의 클래스 내부에 정의 된 메서드는 프로토 타입에 설정되지만 열거 불가능으로 설정됩니다.

Object.getOwnPropertyNames(Object.getPrototypeOf(obj));

0

내 개인 취향의 구현 :)

function getAllProperties(In, Out = {}) {
    const keys = Object.getOwnPropertyNames(In);
    keys.forEach(key => Object.defineProperty(In, key, {
        enumerable: true
    }));
    Out = { ...In, ...Out };

    const Prototype = Object.getPrototypeOf(In);
    return Prototype === Object.prototype ? Out : getAllProperties(Proto, Out);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.