Czy można uzyskać niewliczalne dziedziczone nazwy właściwości obiektu?


99

W JavaScript mamy kilka sposobów uzyskania właściwości obiektu, w zależności od tego, co chcemy uzyskać.

1) Object.keys(), która zwraca wszystkie własne, wyliczalne właściwości obiektu, metodę ECMA5.

2) for...inpętla, która zwraca wszystkie wyliczalne właściwości obiektu, niezależnie od tego, czy są to własności własne, czy też odziedziczone z łańcucha prototypów.

3) Object.getOwnPropertyNames(obj)który zwraca wszystkie własne właściwości obiektu, niewymierne lub nie.

Mamy też takie metody, które hasOwnProperty(prop)pozwalają nam sprawdzić, czy właściwość jest dziedziczona lub faktycznie należy do tego obiektu, a propertyIsEnumerable(prop)co, jak sama nazwa wskazuje, pozwala nam sprawdzić, czy właściwość jest wyliczalna.

Przy tych wszystkich opcjach nie ma możliwości uzyskania niewliczalnej, niepasującej właściwości obiektu, co chcę zrobić. Czy jest na to sposób? Innymi słowy, czy mogę w jakiś sposób uzyskać listę odziedziczonych, niewliczalnych właściwości?

Dziękuję Ci.


4
Twoje pytanie odpowiadało na pytanie, które zamierzałem zadać: Jak sprawdzać niewliczalne właściwości (tylko po to, aby zbadać, co jest dostępne w predefiniowanych obiektach). Wreszcie znalazłem getOwnPropertyNames! :-)
marcus

1
@marcus :-) O to właśnie chodzi w SO!
dkugappi

Odpowiedzi:


115

Ponieważ getOwnPropertyNamesmożna uzyskać niewliczalne właściwości, możesz ich użyć i połączyć z chodzeniem po łańcuchu prototypów.

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
}

Przetestowałem to na Safari 5.1 i otrzymałem

> 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__"]

Aktualizacja: Poprawiono nieco kod (dodano spacje i nawiasy klamrowe oraz poprawiono nazwę funkcji):

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
Dzięki toby, jedna rzecz, której nie rozumiem, to wiersz: while(curr = Object.getPrototypeOf(cure))ponieważ instrukcja warunkowa używa operatora przypisania zamiast operatora porównania, czy to nie zawsze zwróciło prawdę? A może ta linia zasadniczo sprawdza, czy „curr” ma prototyp?
dkugappi

2
@AlexNabokov zwróci fałsz, jeśli wynik jest fałszywy, co nastąpi, gdy Object.getPrototypeOf(cure)powróci nullna górze łańcucha prototypów. Wydaje mi się, że zakłada to brak okrągłych łańcuchów prototypów!
Domenic

2
@Alex Function.prototypenigdy nie może być prototypem „root”, ponieważ wskazuje na to łącze do prototypu Object.prototype. Funkcja Object.getPrototypeOf( obj )zwraca najwyższy obiekt w łańcuchu prototypów obj. Umożliwia śledzenie łańcucha prototypów objaż do jego końca ( nullwartości). Nie jestem pewien, jaki jest twój problem z tym ...
Šime Vidas

2
@Alex Nie, nie jest undefined. Object.getPrototypeOf(John)zwraca Boy.prototypeobiekt (tak jak powinien) - zobacz tutaj: jsfiddle.net/aeGLA/1 . Zauważ, że konstruktor nieBoy znajduje się w łańcuchu prototypów programu . Łańcuch prototyp jest następujący: . JohnJohnBoy.prototype -> Object.prototype -> null
Šime Vidas

3
Pomyślałem, że Object.getPrototypeOf (obj) zwróci prototyp konstruktora obj ” - Tak. W przypadku programu Johnjego konstruktor to Boy, a prototypewłaściwość Boyis Boy.prototype. Więc Object.getPrototypeOf(John)wraca Boy.prototype.
Šime Vidas,

9

Czystsze rozwiązanie wykorzystujące rekursję:

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

Edytować

Bardziej ogólne funkcje:

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

Ten sam szablon można zastosować za pomocą Object.getOwnPropertySymbolsitp.


4

Korzystanie z zestawów prowadzi do nieco czystszego rozwiązania, 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

Prosta iteracja w ES6:

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

Przykładowy bieg:


1

Aby uzyskać wszystkie dziedziczone właściwości lub metody dla jakiejś instancji, możesz użyć czegoś takiego

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
Lepiej używać Object.getInheritedniż Object.prototype.getInherited. Takie postępowanie eliminuje również potrzebę brzydkiego !(name == 'getInherited')czeku. Ponadto w Twojej implementacji propstablica może zawierać zduplikowane właściwości. Na koniec, jaki jest cel ignorowania constructorwłaściwości?
Pauan,

Kiedy atrybut object.getInherited stanie się prawdą? Proszę sprawdzić poniższe pytanie, ponieważ utknąłem w dziedziczeniu: stackoverflow.com/questions/31718345/ ...
Ravindra babu

IMHO - należą one do Reflect, a nie do Object. Lub - alternatywnie - oczekiwałbym od języka Object.keys (src, [settings]), w którym ustawienia opcjonalne mogą określać, czy uwzględniać elementy inne niż ninumerables, czy uwzględniać dziedziczone, czy uwzględniać dziedziczone niewliczalne, czy też własne , jeśli uwzględnić symbole, a może do jakiej maksymalnej głębokości dziedziczenia należy wykopać.
Radagast the Brown

uh ... to samo dla Object.entries. Nie jestem jednak pewien co do wartości Object.values. ...dobrze. Dlaczego nie.
Radagast the Brown

0

Oto rozwiązanie, które wymyśliłem studiując przedmiot. Aby uzyskać wszystkie niepoliczalne, niebędące własnością objobiektu, wykonajgetProperties(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;
}

Przykład użycia:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

jeśli próbujesz zarejestrować niewliczalne właściwości obiektu nadrzędnego, np. Domyślnie metody zdefiniowane w klasie w es6 są ustawiane w prototypie, ale nie są wyliczalne.

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

0

Realizacja w moich osobistych preferencjach :)

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);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.