是否可以获得对象的不可枚举的继承属性名称?
Posted
技术标签:
【中文标题】是否可以获得对象的不可枚举的继承属性名称?【英文标题】:Is it possible to get the non-enumerable inherited property names of an object? 【发布时间】:2011-12-22 20:59:28 【问题描述】:在 javascript 中,我们有几种获取对象属性的方法,具体取决于我们想要获取的内容。
1) Object.keys()
,它返回一个对象的所有自己的、可枚举的属性,一个 ECMA5 方法。
2) 一个for...in
循环,它返回一个对象的所有可枚举属性,无论它们是自己的属性,还是从原型链继承的。
3) Object.getOwnPropertyNames(obj)
返回对象的所有自身属性,无论是否可枚举。
我们还有 hasOwnProperty(prop)
等方法让我们检查属性是否被继承或实际上属于该对象,propertyIsEnumerable(prop)
顾名思义,让我们检查属性是否可枚举。
使用所有这些选项,没有办法获得对象的不可枚举、非自己的属性,这正是我想要做的。有没有办法做到这一点?换句话说,我能以某种方式获取继承的不可枚举属性的列表吗?
谢谢。
【问题讨论】:
您的问题回答了我要问的问题:如何检查不可枚举的属性(只是为了探索预定义对象中可用的内容)。最后我找到了getOwnPropertyNames! :-) @marcus :-) 这就是 SO 的全部意义所在! 【参考方案1】:2022 年 1 月更新 -- 几乎所有答案都加在一起,加上符号
在看到Mozilla's JS documentation specifically say 之后:“没有一种机制可以迭代对象的所有属性;各种机制都包含不同的属性子集。”...我确实有这个问题,尽管它比较新,因为我也想要符号键,我认为以上所有答案都早于那些)。
我来到这里希望其他人知道如何创建这样一个单一的机制。
此页面上似乎没有一个答案可以涵盖所有内容,但在所有答案之间,我认为可以做到——包括排除烦人的top level keys 的选项。
在探索the code in Mozilla's JS doc'n 受到airportyh's answer 启发的过程中,加上the table below it on that same page,我发现了新的Reflect.ownKeys
。这可以捕获所有内容(包括符号)...除了继承的属性,但是 airportyh 走原型链的答案可以解决这个问题。
所以...结合所有这些发现并尽可能简化,我想出了以下两个函数,(我相信)可以捕捉一切。发帖以防万一。
选项 1. 简单:返回每个键,没有例外
返回每个键,无论是否可枚举、字符串、符号、自己的、继承的和***的。
function getAllKeys(obj)
let keys = [];
while (obj)
keys = keys.concat(Reflect.ownKeys(obj));
obj = Object.getPrototypeOf(obj);
return keys;
我真的很喜欢这种简单,但我想知道我是否遗漏了什么。如果有人发现其中有任何错误,请告诉我。
选项 2. 灵活:返回每个键,可选排除项
补充:
-
基于一组单行函数的过滤函数(这种方式更易于调试,这不是代码高尔夫?),根据传入的参数确定是否应排除任何给定键,
是否遍历原型链的条件(根据airportyh's answer),并且,
在达到顶层之前停止或不停止的条件(根据Maciej Krawczyk's answer)。
包含或排除:
可枚举的键 不可枚举的键 符号键 字符串键 自己的钥匙 继承的键 ***键。(顺便说一句,我不是 JS 专家,所以也许我遗漏了一些东西。我有点困惑为什么这里没有其他人使用 Array.prototype.filter(),因为这不正是我们正在做的吗?)
我相信以下内容涵盖了它。默认情况下,所有内容都包括在内,除了***键。调整口味。如果这里有任何错误,我再次欢迎反馈:
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;
【讨论】:
【参考方案2】:您通常不希望包含 Object 原型属性,例如 __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" ]
【讨论】:
【参考方案3】:在 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));
【讨论】:
【参考方案4】:我个人喜好的实现:)
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);
【讨论】:
【参考方案5】:由于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;
【讨论】:
谢谢 toby,我不明白的一件事是:while(curr = Object.getPrototypeOf(cure))
,因为条件语句使用赋值运算符而不是比较运算符,这不总是返回 true 吗?还是这条线本质上是检查“curr”是否有原型?
@AlexNabokov 如果结果为 falsy,它将返回 false,这将在原型链顶部的 Object.getPrototypeOf(cure)
返回 null
时发生。我猜这假设没有圆形原型链!
@Alex Function.prototype
永远不能成为“根”原型,因为它的原型链接指向 Object.prototype
。函数Object.getPrototypeOf( obj )
返回obj
原型链中最顶层的对象。它使您能够遵循obj
的原型链,直到到达其末端(null
值)。我不确定你的问题是什么......
@Alex 不,不是undefined
。 Object.getPrototypeOf(John)
返回 Boy.prototype
对象(应该如此) - 请参见此处:jsfiddle.net/aeGLA/1。请注意,John
的原型链中的构造函数 Boy
not。 John
的原型链如下:Boy.prototype -> Object.prototype -> null
。
"我认为 Object.getPrototypeOf(obj) 会返回 obj 的构造函数原型" - 是的。对于John
,他的构造函数是Boy
,而Boy
的prototype
属性是Boy.prototype
。所以Object.getPrototypeOf(John)
返回Boy.prototype
。【参考方案6】:
使用递归的更简洁的解决方案:
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
等应用相同的模板。
【讨论】:
【参考方案7】:利用 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);
【讨论】:
【参考方案8】:如果您尝试记录父对象 ex 的不可枚举属性。默认情况下,在 es6 中的类中定义的方法在原型上设置,但设置为不可枚举。
Object.getOwnPropertyNames(Object.getPrototypeOf(obj));
【讨论】:
【参考方案9】: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);
【讨论】:
【参考方案10】:这是我在研究该主题时提出的解决方案。要获取 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);
【讨论】:
【参考方案11】:要获取某个实例的所有继承属性或方法,您可以使用类似这样的方法
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
而不是Object.prototype.getInherited
。这样做也消除了对丑陋的!(name == 'getInherited')
检查的需要。此外,在您的实现中,props
数组可以包含重复的属性。最后,忽略constructor
属性的目的是什么?
object.getInherited 什么时候成真?请检查以下问题,因为我坚持继承:***.com/questions/31718345/…
恕我直言-这些属于反射,而不是对象。或者 - 或者 - 我希望从语言 Object.keys(src, [settings]) 中可选设置可以指定是否包括非可数,如果包括继承,如果包括不可枚举继承,如果包括自己, 如果要包含符号,可能还有要挖掘的最大继承深度。
呃...对于 Object.entries 也是如此。虽然不确定 Object.values 。 ...好吧。为什么不呢。以上是关于是否可以获得对象的不可枚举的继承属性名称?的主要内容,如果未能解决你的问题,请参考以下文章