如何在JavaScript中确定对象是否具有给定的属性

如何确定对象x是否具有定义的属性y,而不管x.y的值是多少?

我正在使用

if (typeof(x.y) !== 'undefined')

但这似乎有点笨拙。有没有更好的办法?

312420 次浏览

对象具有以下属性:

如果你正在测试对象本身的属性(不是原型链的一部分),你可以使用.hasOwnProperty():

if (x.hasOwnProperty('y')) {
// ......
}

对象或其原型具有一个属性:

你也可以使用in操作符来测试继承的属性。

if ('y' in x) {
// ......
}

你可以像这样修剪一下:

if ( x.y !== undefined ) ...

如果你想知道对象身体上的是否包含属性@gnarf的,使用hasOwnProperty回答就可以了。

如果你想知道属性是否存在于任何地方,无论是在对象本身还是在原型链中,你可以使用in运营商

if ('prop' in obj) {
// ...
}

如:

var obj = {};


'toString' in obj == true; // inherited from Object.prototype
obj.hasOwnProperty('toString') == false; // doesn't contains it physically

我的原始代码的一个特性

if ( typeof(x.y) != 'undefined' ) ...

在某些情况下可能有用的是,无论x是否存在,使用它都是安全的。对于gnarf回答中的任何一个方法,如果对x是否存在有任何怀疑,应该首先测试它。

因此,也许这三种方法在一个人的魔术包中都有一席之地。

下划线。js或Lodash

if (_.has(x, "y")) ...

:)

既然问题是关于属性检查的笨拙,一个常规的用例是对函数参数选项对象的验证,我想我应该提到一个无库的测试多个属性存在性的简短方法。 免责声明:它确实需要ECMAScript 5(但在我看来,任何仍在使用IE8的人都应该拥有一个破碎的网络)
function f(opts) {
if(!["req1","req2"].every(opts.hasOwnProperty, opts)) {
throw new Error("IllegalArgumentException");
}
alert("ok");
}
f({req1: 123});  // error
f({req1: 123, req2: 456});  // ok

为什么不简单地:

if (typeof myObject.myProperty == "undefined") alert("myProperty is not defined!");

或者如果你想要一个特定的类型:

if (typeof myObject.myProperty != "string") alert("myProperty has wrong type or does not exist!");

ES6 +:

ES6+有一个新功能,你可以像下面这样检查:

if (x?.y)

实际上,解释器检查x的存在,然后调用y,由于在if括号内放置了强制,x?.y转换为布尔值。

包括

Object.keys(x).includes('y');

Array.prototype.includes()方法确定数组在其条目中是否包含某个值,并酌情返回true或false。

而且

Object.keys()返回一个字符串数组,表示给定对象的所有可枚举属性。

.hasOwnProperty()和ES6+ ?. -optional-chaining(如:if (x?.y))也是非常好的2020+选项。

const data = [{"b":1,"c":100},{"a":1,"b":1,"c":150},{"a":1,"b":2,"c":100},{"a":2,"b":1,"c":13}]


const result = data.reduce((r, e)  => {
r['a'] += (e['a'] ? e['a'] : 0)
r['d'] += (e['b'] ? e['b'] : 0)
r['c'] += (e['c'] ? e['c'] : 0)


return r
}, {'a':0, 'd':0, 'c':0})


console.log(result)
`result` { a: 4, d: 5, c: 363 }

与此线程中的其他示例不同,此实现仅断言对象具有我们正在检查的属性。

const hasOwnProperty = <X extends {}, Y extends PropertyKey>(
object: X,
property: Y
): object is Record<Y, unknown> & X => {
return object.hasOwnProperty(property);
};

下面是一个使用它来标识具有所需属性的分支的示例。

const hasOwnProperty = <X extends {}, Y extends PropertyKey>(
object: X,
property: Y
): object is Record<Y, unknown> & X => {
return object.hasOwnProperty(property);
};


type PaidProjectFeeType = 'FIXED_PRICE' | 'RATE' | '%future added value';


const PAID_PROJECT_FEE_TYPE_LABELS: Record<
'FIXED_PRICE' | 'RATE',
string
> = {
FIXED_PRICE: 'Fixed Price',
RATE: 'Rate',
};


export const getPaidProjectFeeTypeLabel = (
feeType: PaidProjectFeeType
): string => {
if (hasOwnProperty(PAID_PROJECT_FEE_TYPE_LABELS, feeType)) {
PAID_PROJECT_FEE_TYPE_LABELS[feeType];
}


throw new Error('test');
};

https://tsplay.dev/m0LBOm

令人恼火的是,现在PAID_PROJECT_FEE_TYPE_LABELS被假定为:

Record<PaidProjectFeeType, unknown> & Record<"FIXED_PRICE" | "RATE", string>

例如,你不能返回结果,因为X[Y]的一个可能值是unknown。当您需要断言对象具有所需的属性时,这很有用,但您将需要添加进一步的断言以确保结果是您想要的。

不过,有一个更好的办法。

我们需要两个工具:

export const keys = <T extends Record<string, unknown>>(
object: T
): Array<keyof T> => {
return Object.keys(object);
};

keys为我们提供了对象属性名的类型化数组。

export const includes = <C extends M, M>(
collection: readonly C[],
member: M
): member is C => {
return collection.includes(member as C);
};

includes允许断言一个属性是只读数组的成员。你可以在博客中阅读更多关于includes的内容。

export const keys = <T extends Record<string, unknown>>(
object: T
): Array<keyof T> => {
return Object.keys(object);
};


export const includes = <C extends M, M>(
collection: readonly C[],
member: M
): member is C => {
return collection.includes(member as C);
};


type PaidProjectFeeType = 'FIXED_PRICE' | 'RATE' | '%future added value';


const PAID_PROJECT_FEE_TYPE_LABELS: Record<
'FIXED_PRICE' | 'RATE',
string
> = {
FIXED_PRICE: 'Fixed Price',
RATE: 'Rate',
};


export const getPaidProjectFeeTypeLabel = (
feeType: PaidProjectFeeType
): string => {
if (includes(keys(PAID_PROJECT_FEE_TYPE_LABELS), feeType)) {
return PAID_PROJECT_FEE_TYPE_LABELS[feeType];
}


throw new Error('test');
};

https://tsplay.dev/N7gLDN

简而言之,这种方法允许我们将feeType的值缩小到keys(PAID_PROJECT_FEE_TYPE_LABELS)中的值,然后允许我们访问属性值。

这种方法工作得最好,但需要注意的是,技术上keys实现不是运行时安全的。在运行时返回的值与使用tsc推断的值不同的情况下(主要是理论性的)。

除了其他答案,我想建议使用Object.hasOwn()方法来检查指定的对象是否具有指定的属性作为自己的属性(在对象本身上的意思),您可以使用新的Object.hasOwn()方法,这是一个静态方法,如果指定的对象具有指定的属性作为自己的属性,则返回true。如果该属性被继承,或不存在,则该方法返回false。

const person = { name: 'dan' };


console.log(Object.hasOwn(person, 'name'));// true
console.log(Object.hasOwn(person, 'age'));// false


const person2 = Object.create({gender: 'male'});


console.log(Object.hasOwn(person2, 'gender'));// false

建议在Object.hasOwnProperty()上使用此方法,因为它也适用于使用Object.create(null)创建的对象和覆盖继承的hasOwnProperty()方法的对象。虽然可以通过在外部对象上调用Object.prototype.hasOwnProperty()来解决这类问题,但Object.hasOwn()克服了这些问题,因此是首选方法(参见下面的示例)

let person = {
hasOwnProperty: function() {
return false;
},
age: 35
};


if (Object.hasOwn(person, 'age')) {
console.log(person.age); // true - the remplementation of hasOwnProperty() did not affect the Object
}

let person = Object.create(null);
person.age = 35;
if (Object.hasOwn(person, 'age')) {
console.log(person.age); // true - works regardless of how the object was created
}

关于Object.hasOwn的更多信息可以在这里找到:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn

浏览器兼容性——https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn#browser_compatibility

检查指定的属性是否存在于对象本身或原型链中,可以通过in操作符验证,正如其他答案所建议的那样。