Apakah mungkin untuk mendapatkan nama properti warisan yang tidak dapat dihitung dari suatu objek?


99

Dalam JavaScript kita memiliki beberapa cara untuk mendapatkan properti dari suatu objek, bergantung pada apa yang ingin kita dapatkan.

1) Object.keys(), yang mengembalikan semua properti objek yang tak terhitung jumlahnya, metode ECMA5.

2) for...inperulangan, yang mengembalikan semua properti yang dapat dihitung dari suatu objek, terlepas dari apakah itu properti sendiri, atau diwarisi dari rantai prototipe.

3) Object.getOwnPropertyNames(obj)yang mengembalikan semua properti sendiri dari suatu objek, dapat dihitung atau tidak.

Kami juga memiliki metode seperti hasOwnProperty(prop)memungkinkan kami memeriksa apakah properti diwariskan atau benar-benar milik objek itu, dan propertyIsEnumerable(prop)yang, seperti namanya, memungkinkan kami memeriksa apakah properti dapat dihitung.

Dengan semua opsi ini, tidak ada cara untuk mendapatkan properti non-enumerable, non-own dari suatu objek, yang ingin saya lakukan. Apakah ada cara untuk melakukan ini? Dengan kata lain, dapatkah saya mendapatkan daftar properti warisan yang tidak dapat dihitung?

Terima kasih.


4
Pertanyaan Anda menjawab pertanyaan yang akan saya tanyakan: Bagaimana cara memeriksa properti yang tidak dapat dihitung (hanya untuk menjelajahi apa yang tersedia di objek yang telah ditentukan sebelumnya). Akhirnya saya menemukan getOwnPropertyNames! :-)
marcus

1
@marcus :-) Itulah SO itu semua!
dkugappi

Jawaban:


115

Karena getOwnPropertyNamesdapat memberi Anda properti yang tidak dapat dihitung, Anda dapat menggunakannya dan menggabungkannya dengan menjalankan rantai prototipe.

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
}

Saya mengujinya di Safari 5.1 dan mendapatkan

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

Pembaruan: Memperbaiki kode sedikit (spasi tambahan, dan kurung kurawal, dan meningkatkan nama fungsi):

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
Berkat toby, satu hal yang saya tidak mengerti adalah barisnya:, while(curr = Object.getPrototypeOf(cure))karena pernyataan bersyarat menggunakan operator penugasan alih-alih operator perbandingan, bukankah ini selalu mengembalikan nilai true? Atau baris ini pada dasarnya memeriksa apakah "current" memiliki prototipe?
dkugappi

2
@AlexNabokov itu akan mengembalikan false jika hasilnya salah, yang akan terjadi ketika Object.getPrototypeOf(cure)kembali nulldi bagian atas rantai prototipe. Saya kira ini mengasumsikan tidak ada rantai prototipe melingkar!
Domenic

2
@Alex Function.prototypetidak pernah bisa menjadi prototipe "root", karena itu adalah prototipe link yang mengarah ke Object.prototype. Fungsi Object.getPrototypeOf( obj )mengembalikan objek paling atas dalam rantai prototipe obj. Ini memungkinkan Anda untuk mengikuti rantai prototipe objhingga Anda mencapai akhirnya ( nullnilainya). Saya tidak yakin apa masalah Anda dengan ini ...
Šime Vidas

2
@Alex Tidak, tidak undefined. Object.getPrototypeOf(John)mengembalikan Boy.prototypeobjek (sebagaimana mestinya) - lihat di sini: jsfiddle.net/aeGLA/1 . Perhatikan bahwa constructor Boyadalah tidak dalam rantai prototipe John. Rantai prototipe dari Johnadalah sebagai berikut: Boy.prototype -> Object.prototype -> null.
Šime Vidas

3
" Saya pikir Object.getPrototypeOf (obj) akan mengembalikan prototipe konstruktor obj " - Ya. Dalam kasus John, konstruktornya adalah Boy, dan prototypeproperti dari Boyadalah Boy.prototype. Jadi Object.getPrototypeOf(John)kembali Boy.prototype.
Šime Vidas

9

Solusi yang lebih bersih menggunakan rekursi:

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

Edit

Fungsi yang lebih umum:

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

Template yang sama ini dapat diterapkan menggunakan Object.getOwnPropertySymbols, dll.


4

Memanfaatkan Sets mengarah ke solusi yang agak lebih bersih, 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

Iteratif lurus ke depan di ES6:

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

Contoh run:


1

Untuk mendapatkan semua properti atau metode yang diwariskan untuk beberapa contoh, Anda dapat menggunakan sesuatu seperti ini

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
Lebih baik digunakan Object.getInheriteddaripada Object.prototype.getInherited. Melakukan itu juga menghilangkan kebutuhan akan !(name == 'getInherited')cek jelek . Selain itu, dalam implementasi Anda, propslarik dapat berisi properti duplikat. Terakhir, apa tujuan mengabaikan constructorproperti?
Pauan

Kapan object.getInherited akan menjadi true? Silakan periksa pertanyaan di bawah ini karena saya terjebak dengan warisan: stackoverflow.com/questions/31718345/…
Ravindra babu

IMHO - ini milik Reflect, bukan Object. Atau - sebagai alternatif - saya harapkan dari bahasa Object.keys (src, [pengaturan]) di mana pengaturan opsional dapat menentukan apakah menyertakan non-ninumerables, jika menyertakan warisan, jika menyertakan warisan non-enumerable, jika menyertakan milik sendiri , jika untuk memasukkan simbol, dan mungkin untuk apa kedalaman warisan maksimal untuk digali.
Radagast the Brown

uh ... sama untuk Object.entries. Tidak yakin tentang Object.values. ...baik. kenapa tidak.
Radagast the Brown

0

Berikut adalah solusi yang saya dapatkan saat mempelajari subjek. Untuk mendapatkan semua properti non-milik non-enumerable dari objobjek lakukangetProperties(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;
}

Contoh penggunaan:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

jika Anda mencoba untuk membuat log properti yang tidak dapat dihitung dari objek induk misalnya. secara default metode yang didefinisikan di dalam kelas di es6 diatur pada prototipe tetapi ditetapkan sebagai non-enumerable.

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

0

Penerapan dalam preferensi pribadi saya :)

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);
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.