Bir nesnenin numaralandırılamayan miras alınan özellik adlarını almak mümkün müdür?


103

JavaScript'te, ne elde etmek istediğimize bağlı olarak, bir nesnenin özelliklerini elde etmenin birkaç yolu vardır.

1) Object.keys(), bir nesnenin tüm kendi numaralandırılabilir özelliklerini, bir ECMA5 yöntemini döndürür.

2) for...inister kendi özellikleri olsun, isterse prototip zincirinden miras alınmış olsun, bir nesnenin tüm numaralandırılabilir özelliklerini döndüren bir döngü.

3) Object.getOwnPropertyNames(obj)bir nesnenin tüm kendi özelliklerini, numaralandırılabilir olsun ya da olmasın döndürür.

Ayrıca, hasOwnProperty(prop)bir özelliğin miras alınıp alınmadığını veya gerçekten o nesneye ait olup olmadığını propertyIsEnumerable(prop)kontrol etmemizi sağlayan ve adından da anlaşılacağı gibi, bir özelliğin numaralandırılabilir olup olmadığını kontrol etmemizi sağlayan yöntemlere sahibiz .

Tüm bu seçeneklerle, bir nesnenin numaralandırılamayan, kendi olmayan bir özelliğini elde etmenin bir yolu yok , ki bunu yapmak istiyorum. Bunu yapmanın bir yolu var mı? Başka bir deyişle, miras alınan numaralandırılamayan özelliklerin bir listesini bir şekilde alabilir miyim?

Teşekkür ederim.


4
Sorunuz, soracağım soruyu yanıtladı: Numaralandırılamayan özellikler nasıl incelenir (sadece önceden tanımlanmış nesnelerde neyin mevcut olduğunu keşfetmek için). Sonunda getOwnPropertyNames! :-)
marcus

1
@marcus :-) SO işte budur!
dkugappi

Yanıtlar:


115

Yana getOwnPropertyNamesolmayan enumerable özelliklerini alabilirsiniz, bunu kullanmak ve prototip zincirini yukarı yürüyen ile birleştirebilirsiniz.

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
}

Bunu Safari 5.1'de test ettim ve

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

Güncelleme: Kodu biraz yeniden düzenledi (boşluklar ve kaşlı ayraçlar eklendi ve işlev adı iyileştirildi):

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
Toby sayesinde, anlamadığım bir şey şu satır: while(curr = Object.getPrototypeOf(cure))Koşullu ifade, bir karşılaştırma operatörü yerine bir atama operatörü kullandığından, bu her zaman doğru olmaz mı? Yoksa bu satır aslında "curr" ın bir prototipi olup olmadığını mı kontrol ediyor?
dkugappi

2
@AlexNabokov sonuç yanlışsa, prototip zincirinin tepesinde Object.getPrototypeOf(cure)döndüğünde ortaya çıkacaktır null. Sanırım bu, dairesel prototip zincirleri olmadığını varsayıyor!
Domenic

2
@Alex Function.prototype, prototip bağlantısı gösterdiği için asla "kök" prototip olamaz Object.prototype. İşlev Object.getPrototypeOf( obj ), prototip zincirindeki en üstteki nesneyi döndürür obj. objSonuna ( nulldeğerine) ulaşana kadar prototip zincirini takip etmenizi sağlar . Bununla ilgili sorunun ne olduğundan emin değilim ...
Šime Vidas

2
@Alex Hayır, değil undefined. nesneyi Object.getPrototypeOf(John)döndürür Boy.prototype(olması gerektiği gibi) - buraya bakın: jsfiddle.net/aeGLA/1 . Yapıcı unutmayın Boyolduğu değil prototip zincirinde John. Prototip zinciri Johnaşağıdaki gibidir: Boy.prototype -> Object.prototype -> null.
Šime Vidas

3
" Object.getPrototypeOf (obj) 'in nesnenin kurucusunun prototipini döndüreceğini düşündüm " - Evet. Durumunda, Johnkurucusu Boyve prototypemülkü Boyis'dir Boy.prototype. Yani Object.getPrototypeOf(John)döner Boy.prototype.
Šime Vidas

11

Özyineleme kullanan daha temiz bir çözüm:

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

Düzenle

Daha genel işlevler:

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

Aynı şablon Object.getOwnPropertySymbols, vb. Kullanılarak uygulanabilir .


4

Setlerden yararlanmak biraz daha temiz bir çözüm olan IMO'ya yol açar.

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'da doğrudan yinelemeli:

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

Örnek çalışma:


1

Bazı örnekler için tüm miras alınan özellikleri veya yöntemleri almak için bunun gibi bir şey kullanabilirsiniz.

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
Kullanmak Object.getInheritedyerine daha iyi Object.prototype.getInherited. Bunu yapmak aynı zamanda çirkin !(name == 'getInherited')kontrol ihtiyacını da ortadan kaldırır . Ayrıca, uygulamanızda propsdizi yinelenen özellikler içerebilir. Son olarak, constructormülkü görmezden gelmenin amacı nedir?
Pauan

Object.getInherited ne zaman gerçek olacak? Kalıtımla sıkışıp kaldığım için lütfen aşağıdaki soruyu kontrol edin: stackoverflow.com/questions/31718345/…
Ravindra babu

IMHO - bunlar Object'e değil Reflect'e aittir. Veya - alternatif olarak - isteğe bağlı ayarların, sayılamayanların dahil edilip edilmeyeceğini, miras alınanların dahil edilip edilmeyeceğini, kendi sayılamayan mirasın dahil edilip edilmeyeceğini belirtebileceği Object.keys (src, [ayarlar]) dilinden beklerim. , semboller dahil edilip edilmeyeceği ve belki de hangi maksimum miras derinliğine kadar kazılacağı.
Radagast Brown

uh ... Object.entries için aynı. Yine de Object.values ​​hakkında emin değilim. ...iyi. neden olmasın.
Radagast Brown

0

İşte konuyu incelerken bulduğum çözüm. objNesnenin tüm numaralandırılamayan kendine ait olmayan özelliklerini elde etmek içingetProperties(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;
}

Kullanım örneği:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

bir üst nesnenin numaralandırılamayan özelliklerini günlüğe kaydetmeye çalışıyorsanız ex. varsayılan olarak es6'da bir sınıf içinde tanımlanan yöntemler prototip üzerinde ayarlanır, ancak numaralandırılamaz olarak ayarlanır.

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

0

Kişisel tercihlerimde bir uygulama :)

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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.