Có thể lấy các tên thuộc tính kế thừa không liệt kê được của một đối tượng không?


99

Trong JavaScript, chúng ta có một số cách để lấy các thuộc tính của một đối tượng, tùy thuộc vào những gì chúng ta muốn lấy.

1) Object.keys(), trả về tất cả các thuộc tính riêng, có thể liệt kê của một đối tượng, một phương thức ECMA5.

2) một for...invòng lặp, trả về tất cả các thuộc tính có thể liệt kê của một đối tượng, bất kể chúng là thuộc tính riêng hay được kế thừa từ chuỗi nguyên mẫu.

3) Object.getOwnPropertyNames(obj)trả về tất cả các thuộc tính riêng của một đối tượng, có thể liệt kê hoặc không.

Chúng tôi cũng có các phương thức như hasOwnProperty(prop)cho phép chúng tôi kiểm tra xem một thuộc tính được kế thừa hay thực sự thuộc về đối tượng đó, và propertyIsEnumerable(prop)như tên cho thấy, cho phép chúng tôi kiểm tra xem một thuộc tính có thể liệt kê được hay không.

Với tất cả các tùy chọn này, không có cách nào để có được thuộc tính không thể liệt kê, không thuộc sở hữu của một đối tượng, đó là điều tôi muốn làm. Có cách nào để làm điều này? Nói cách khác, bằng cách nào đó tôi có thể lấy danh sách các thuộc tính không liệt kê được kế thừa không?

Cảm ơn bạn.


4
Câu hỏi của bạn đã trả lời câu hỏi mà tôi sẽ hỏi: Làm thế nào để kiểm tra các thuộc tính không liệt kê (chỉ để khám phá những gì có sẵn trong các đối tượng được xác định trước). Cuối cùng tôi đã tìm thấy getOwnPropertyNames! :-)
marcus

1
@marcus :-) Đó là tất cả về SO!
dkugappi 21/12/11

Câu trả lời:


115

getOwnPropertyNamescó thể mang lại cho bạn các thuộc tính không thể liệt kê, bạn có thể sử dụng nó và kết hợp nó với việc đi lên chuỗi nguyên mẫu.

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
}

Tôi đã thử nghiệm điều đó trên Safari 5.1 và nhận được

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

Cập nhật: Đã cấu trúc lại mã một chút (đã thêm dấu cách và dấu ngoặc nhọn, và cải thiện tên hàm):

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
Cảm ơn bạn, có một điều tôi không hiểu là dòng:, while(curr = Object.getPrototypeOf(cure))vì câu lệnh điều kiện sử dụng toán tử gán thay vì toán tử so sánh, điều này có phải lúc nào cũng trả về true không? Hay dòng này thực chất là kiểm tra xem "curr" có nguyên mẫu hay không?
dkugappi

2
@AlexNabokov nó sẽ trả về false nếu kết quả là sai, điều này sẽ xảy ra khi Object.getPrototypeOf(cure)trả về nullở đầu chuỗi nguyên mẫu. Tôi đoán điều này giả định không có chuỗi nguyên mẫu hình tròn!
Domenic

2
@Alex Function.prototypekhông bao giờ có thể là nguyên mẫu "gốc", vì liên kết nguyên mẫu của nó trỏ tới Object.prototype. Hàm Object.getPrototypeOf( obj )trả về đối tượng trên cùng trong chuỗi nguyên mẫu của obj. Nó cho phép bạn theo dõi chuỗi nguyên mẫu objcho đến khi bạn đạt đến phần cuối của nó ( nullgiá trị). Tôi không chắc vấn đề của bạn với vấn đề này là gì ...
Šime Vidas

2
@Alex Không, không phải undefined. Object.getPrototypeOf(John)trả về Boy.prototypeđối tượng (như nó phải) - xem tại đây: jsfiddle.net/aeGLA/1 . Lưu ý rằng các nhà xây dựng Boykhông trong chuỗi nguyên mẫu của John. Chuỗi ban đầu của Johnthực hiện như sau: Boy.prototype -> Object.prototype -> null.
Šime Vidas

3
" Tôi đã nghĩ Object.getPrototypeOf (obj) sẽ trả về nguyên mẫu của hàm tạo của obj " - Có. Trong trường hợp của John, hàm tạo của anh ta là Boy, và thuộc prototypetính của BoyBoy.prototype. Vì vậy, Object.getPrototypeOf(John)lợi nhuận Boy.prototype.
Šime Vidas

9

Một giải pháp sạch hơn sử dụng đệ quy:

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

Biên tập

Các chức năng chung khác:

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

Có thể áp dụng cùng một mẫu này bằng cách sử dụng Object.getOwnPropertySymbols, v.v.


4

Tận dụng các Bộ dẫn đến một giải pháp gọn gàng hơn, 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

Lặp lại thẳng về phía trước trong ES6:

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

Chạy ví dụ:


1

Để có được tất cả các thuộc tính hoặc phương thức kế thừa trong một số trường hợp, bạn có thể sử dụng một cái gì đó như thế này

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
Tốt hơn để sử dụng Object.getInheritedhơn là Object.prototype.getInherited. Làm điều đó cũng loại bỏ nhu cầu !(name == 'getInherited')kiểm tra xấu xí . Ngoài ra, trong quá trình triển khai của bạn, propsmảng có thể chứa các thuộc tính trùng lặp. Cuối cùng, mục đích của việc bỏ qua constructortài sản là gì?
Pauan

Khi nào thì object.getInhe inherit trở thành true? Vui lòng kiểm tra bên dưới câu hỏi như tôi đang mắc kẹt với thừa kế: stackoverflow.com/questions/31718345/...
Ravindra babu

IMHO - những thứ này thuộc về Reflect, không thuộc về Object. Hoặc - cách khác - tôi mong đợi từ ngôn ngữ Object.keys (src, [settings]) trong đó cài đặt tùy chọn có thể chỉ định xem có bao gồm không-ninumerables không, nếu bao gồm kế thừa, nếu bao gồm không liệt kê được kế thừa, nếu bao gồm riêng , nếu bao gồm các ký hiệu, và có thể là độ sâu thừa kế tối đa để đào.
Radagast the Brown

uh ... tương tự đối với Object.entries. Tuy nhiên, không chắc chắn về Object.values. ...tốt. tại sao không.
Radagast the Brown

0

Đây là giải pháp mà tôi đã đưa ra khi nghiên cứu đề tài này. Để có được tất cả các thuộc tính không phải của objđối tượng có thể liệt kê đượcgetProperties(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;
}

Ví dụ về việc sử dụng:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

nếu bạn đang cố gắng ghi lại các thuộc tính không liệt kê được của một đối tượng mẹ, ví dụ: theo mặc định, các phương thức được định nghĩa bên trong một lớp trong es6 được đặt trên nguyên mẫu nhưng được đặt là không thể liệt kê.

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

0

Một triển khai theo sở thích cá nhân của tôi :)

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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.