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

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

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

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

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

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

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

감사합니다.



답변

때문에 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;
}


답변

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

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등을 사용하여 적용 할 수 있습니다 .


답변

세트를 활용하면 다소 깨끗한 솔루션 인 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);
}


답변

ES6의 간단한 반복 :

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

실행 예 :


답변

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

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(","));


답변

주제를 공부하면서 생각 해낸 해결책은 다음과 같습니다. 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);
}


답변

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);