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


99

在JavaScript中,根据要获取的内容,我们有几种获取对象属性的方法。

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

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

3)Object.getOwnPropertyNames(obj)返回对象本身的所有属性,无论是否可枚举。

我们还拥有诸如hasOwnProperty(prop)让我们检查属性是否继承或实际上属于该对象之类的方法,以及propertyIsEnumerable(prop)顾名思义,可以让我们检查属性是否可枚举的方法。

使用所有这些选项,就无法获得对象的不可枚举,非自有属性,这就是我想要做的。有什么办法吗?换句话说,我能以某种方式获得继承的不可枚举属性的列表吗?

谢谢。


4
您的问题回答了我要问的问题:如何检查不可枚举的属性(只是为了探索预定义对象中的可用属性)。最后我找到了getOwnPropertyNames!:-)
marcus

1
@marcus :-)这就是全部内容!
dkugappi 2011年

Answers:


115

由于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;
}

1
感谢toby,我不明白的是这一行:while(curr = Object.getPrototypeOf(cure))由于条件语句使用赋值运算符而不是比较运算符,难道这不总是返回true吗?还是该行本质上是在检查“ curr”是否具有原型?
dkugappi

2
@AlexNabokov如果结果为假,则将返回false,这将在原型链顶部Object.getPrototypeOf(cure)返回null时发生。我猜这是假设没有圆形原型链的!
Domenic

2
@Alex Function.prototype永远不是“根”原型,因为它的原型链接指向Object.prototype。该函数Object.getPrototypeOf( obj )返回的原型链中的最高对象obj。它使您能够遵循的原型链,obj直到达到终点为止(null价值)。我不知道这个问题你是...
森那维达斯

2
@Alex不,不是undefinedObject.getPrototypeOf(John)返回Boy.prototype对象(应该如此)-参见此处:jsfiddle.net/aeGLA/1。注意构造Boy不是在原型链John。的原型链John如下:Boy.prototype -> Object.prototype -> null
森那维达斯

3
我以为Object.getPrototypeOf(obj)将返回obj的构造函数的原型 ”-是的。在的情况下John,他的构造函数Boyprototype财产Boy就是Boy.prototype。因此Object.getPrototypeOf(John)返回Boy.prototype
森那维达斯

9

使用递归的更干净的解决方案:

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


4

利用Set可以带来更清洁的解决方案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

ES6中的直接迭代:

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

示例运行:


1

要获取某个实例的所有继承的属性或方法,可以使用如下所示的方法

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
最好使用Object.getInherited而不是Object.prototype.getInherited。这样做也消除了对丑陋!(name == 'getInherited')检查的需要。同样,在您的实现中,props数组可以包含重复的属性。最后,忽略该constructor物业的目的是什么?
Pauan

什么时候object.getInherited将变为true?:请检查下面的问题,因为我坚持继承stackoverflow.com/questions/31718345/...
拉温德拉巴布

恕我直言-这些属于Reflect,而不是Object。或者-或者-我期望从Object.keys(src,[settings])语言开始,在该语言中,可选设置可以指定是否包括非数字,是否包含继承,是否包括非枚举继承,是否包括自己的继承,如果要包含符号,也许要挖掘到最大继承深度。
布朗·拉格斯塔斯特

呃... Object.entries也一样。虽然不确定Object.values。...好。为什么不。
布朗·拉达加斯特

0

这是我在学习本主题时想出的解决方案。要获取对象的所有不可枚举的非拥有属性,obj请执行getProperties(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;
}

使用示例:

function MakeA(){

}

var a = new MakeA();

var arNonEnumerable = getNonEnumerableNonOwnPropertyNames(a);

0

如果您尝试记录父对象ex的不可枚举的属性。默认情况下,在es6中的类内定义的方法是在原型上设置的,但设置为不可枚举的。

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

0

根据我的个人喜好实现:)

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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.