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

在 JavaScript 中,我们有几种获取对象属性的方法,这取决于我们想要获取什么。

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

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

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

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

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

谢谢你。

24155 次浏览

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

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(","));

因为 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
}


console.log(getAllProperties([1,2,3]));

我在 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;
}

Here is the solution that I came up with while studying the subject. To get all non-enumerable non-own properties of the obj object do 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);
}
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);

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

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

利用集的优势导致一个有点干净的解决方案,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);
}

一个使用递归的简洁解决方案:

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

示例运行:

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


let obj = {
abc: 123,
xyz: 1.234,
foobar: "hello"
};


console.log(getAllPropertyNames(obj));

我个人偏好中的一个实现:)

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

您通常不希望包括对象原型属性,如 __defineGetter__hasOwnProperty__proto__等。

此实现允许您包含或排除对象原型属性:

function getAllPropertyNames(object, includeObjectPrototype = false) {
const props = Object.getOwnPropertyNames(object);


let proto = Object.getPrototypeOf(object);
const objectProto = Object.getPrototypeOf({});


while (proto && (includeObjectPrototype || proto !== objectProto)) {
for (const prop of Object.getOwnPropertyNames(proto)) {
if (props.indexOf(prop) === -1) {
props.push(prop);
}
}
proto = Object.getPrototypeOf(proto);
}


return props;
}


console.log(getAllPropertyNames(new Error('Test'), true));
// ["fileName", "lineNumber", "columnNumber", "message", "toString", "name", "stack", "constructor", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__"]
console.log(getAllPropertyNames(new Error('Test'), false));
// [ "fileName", "lineNumber", "columnNumber", "message", "toString", "name", "stack", "constructor" ]

2022年1月更新——几乎所有的答案都加上了符号(ES6 +)

在看到 Mozilla 的 JS 文档特别指出之后: “没有一种机制可以迭代一个对象的所有属性; 各种机制都包含不同的属性子集。”... 我有这个问题,虽然更新,因为我想符号键也,我认为所有的答案以上的日期之前)。

I came here hoping someone else knew how to create such a single mechanism.

在这个页面上没有一个单一的答案似乎涵盖了所有的东西,但是在所有的答案之间,我认为这是可以做到的——包括选项也排除令人讨厌的 顶层钥匙

在探索 Mozilla 的 JS doc’n 中的代码airportyh's answer启发,加上 在同一页下面的表格我发现了新的 Reflect.ownKeys。除了继承的属性之外,它捕捉到了所有的东西(包括符号) ,但是 Airportyh 的答案在原型链中遍历修复了这个问题。

所以... 结合所有这些发现和尽可能简化,我想出了以下两个函数,(我相信)确实捕捉到了一切。发布信息,以防对其他人有帮助。

选项1。简单: 返回每个键,没有例外

返回每个键,可枚举与否、字符串、符号、自有、继承和顶级。

function getAllKeys(obj) {
let keys = [];
// if primitive (primitives still have keys) skip the first iteration
if (!(obj instanceof Object)) {
obj = Object.getPrototypeOf(obj)
}
while (obj) {
keys = keys.concat(Reflect.ownKeys(obj));
obj = Object.getPrototypeOf(obj);
}
return keys;
}

我真的很喜欢这种简单,虽然我不知道我是否错过了什么。如果有人发现任何错误,请告诉我。

选项2: 灵活: 返回每个键,可选排除

补充:

  1. 一个基于一系列单行函数的过滤器函数(这样更容易调试,而且这不是代码高尔夫) ,它根据传入的参数决定是否应该排除任何给定的键,
  2. 在原型链上行走或不行走的条件(按 Airportyh 的回答) ,
  3. 在达到最高水平之前停止或不停止的条件(按 Maciej Krawczyk 的回答)。

Include or exclude:

  • 可枚举键
  • 不可枚举密钥不可枚举密钥
  • 符号键
  • 字符串键
  • 自己的钥匙
  • 继承的钥匙
  • 顶层钥匙。

(On a side note, I'm no JS expert, so maybe I'm missing something. I'm a little confused why no one else here has used Array.prototype.filter(), since isn't that exactly what we're doing?)

我相信以下内容涵盖了它。默认情况下,除了顶级键之外,所有内容都包含在内。根据口味调整。如果这里有任何错误,我再次欢迎反馈:

function getAllKeysConditionally(obj, includeSelf = true, includePrototypeChain = true, includeTop = false, includeEnumerables = true, includeNonenumerables = true, includeStrings = true, includeSymbols = true) {
    

// Boolean (mini-)functions to determine any given key's eligibility:
const isEnumerable = (obj, key) => Object.propertyIsEnumerable.call(obj, key);
const isString = (key) => typeof key === 'string';
const isSymbol = (key) => typeof key === 'symbol';
const includeBasedOnEnumerability = (obj, key) => (includeEnumerables && isEnumerable(obj, key)) || (includeNonenumerables && !isEnumerable(obj, key));
const includeBasedOnKeyType = (key) => (includeStrings && isString(key)) || (includeSymbols && isSymbol(key));
const include = (obj, key) => includeBasedOnEnumerability(obj, key) && includeBasedOnKeyType(key);
const notYetRetrieved = (keys, key) => !keys.includes(key);
    

// filter function putting all the above together:
const filterFn = key => notYetRetrieved(keys, key) && include(obj, key);
    

// conditional chooses one of two functions to determine whether to exclude the top level or not:
const stopFn = includeTop ? (obj => obj === null) : (obj => Object.getPrototypeOf(obj) === null);
    

// and now the loop to collect and filter everything:
let keys = [];
while (!stopFn(obj, includeTop)) {
if (includeSelf) {
const ownKeys = Reflect.ownKeys(obj).filter(filterFn);
keys = keys.concat(ownKeys);
}
if (!includePrototypeChain) { break; }
else {
includeSelf = true;
obj = Object.getPrototypeOf(obj);
}
}
return keys;
}

正如 Jeff Hykin 在评论中指出的那样,这些解决方案使用了 ES6中新增的 Reflect < em > 和箭头函数。因此,ES6最低要求。