是否可以获取对象的不可枚举的继承属性名称?

IT技术 javascript oop object properties
2021-02-01 13:13:36

在 JavaScript 中,我们有几种获取对象属性的方法,具体取决于我们想要获取的内容。

1) Object.keys(),它返回一个对象的所有自己的、可枚举的属性,一个 ECMA5 方法。

2) 一个for...in循环,它返回对象的所有可枚举属性,无论它们是自己的属性,还是从原型链继承的。

3)Object.getOwnPropertyNames(obj)返回对象的所有属性,可枚举或不可枚举。

我们也有这样的方法,hasOwnProperty(prop)让我们检查一个属性是否被继承或实际上属于那个对象,propertyIsEnumerable(prop)顾名思义,让我们检查一个属性是否可枚举。

使用所有这些选项,无法获得对象的不可枚举、非拥有的属性,这正是我想要做的。有没有办法做到这一点?换句话说,我可以以某种方式获得继承的不可枚举属性的列表吗?

谢谢你。

6个回答

由于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;
}
感谢托比,我不明白的一件事是:while(curr = Object.getPrototypeOf(cure)),因为条件语句使用赋值运算符而不是比较运算符,这不是总是返回 true 吗?或者这条线本质上是检查“curr”是否有原型?
2021-03-12 13:13:36
@Alex 不,不是undefinedObject.getPrototypeOf(John)返回Boy.prototype对象(应该如此) - 请参阅此处:jsfiddle.net/aeGLA/1注意构造Boy不是在原型链John的原型链John如下:Boy.prototype -> Object.prototype -> null
2021-03-16 13:13:36
我认为 Object.getPrototypeOf(obj) 会返回 obj 的构造函数的原型” - 是的。在 的情况下John,他的构造函数是Boy,而 的prototype属性BoyBoy.prototype所以Object.getPrototypeOf(John)返回Boy.prototype
2021-03-25 13:13:36
@AlexNabokov 如果结果为假,它将返回假,这将在原型链的顶部Object.getPrototypeOf(cure)返回null发生我想这假设没有圆形原型链!
2021-03-26 13:13:36
@AlexFunction.prototype永远不可能是“根”原型,因为它的原型链接指向Object.prototype. 该函数Object.getPrototypeOf( obj )返回 的原型链中最顶层的对象obj它使您能够遵循 的原型链,obj直到到达其终点(nullvalue)。我不确定你的问题是什么......
2021-04-01 13:13:36

使用递归的更清洁的解决方案:

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来应用相同的模板

ES6 中的直接迭代:

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

示例运行:

利用 Sets 会带来更简洁的解决方案 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);
}

要获取某些实例的所有继承属性或方法,您可以使用这样的方法

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(","));
object.getInherited 什么时候会变成真的?请检查以下问题,因为我坚持继承:stackoverflow.com/questions/31718345/...
2021-03-21 13:13:36
更好地使用Object.getInherited而不是Object.prototype.getInherited. 这样做也消除了对丑陋!(name == 'getInherited')检查的需要此外,在您的实现中,props数组可以包含重复的属性。最后,忽略constructor属性的目的是什么?
2021-03-22 13:13:36
呃...... Object.entries 也是如此。虽然不确定 Object.values 。...好。为什么不。
2021-03-28 13:13:36
恕我直言 - 这些属于反射,而不是对象。或者 - 或者 - 我希望从语言 Object.keys(src, [settings]) 那里可选设置可以指定是否包含不可数的,如果包含继承的,如果包含不可枚举的继承,如果包含自己的,如果要包含符号,也许要挖掘的最大继承深度是多少。
2021-04-09 13:13:36