如何在JavaScript中检查对象是否具有特定属性?

1818

如何在JavaScript中检查对象是否具有特定属性?

考虑以下代码:

x = {'key': 1};
if ( x.hasOwnProperty('key') ) {
    //Do this
}

那是最好的做法吗?


31
我写了一个 jsperf 测试,用大家的答案来看哪个最快:http://jsperf.com/dictionary-contains-key - styfle
('propertyName' in Object) ? '属性存在' : '属性不存在' - Mohan Ram
2
@styfle 感谢你提供的jsperf测试。对我来说,“in”和“hasOwnProperty”的速度比其他方法慢得多(慢了98%)。我对“hasOwnProperty”较慢并不感到惊讶,但我对“in”较慢感到惊讶。 - evanrmurphy
3
有一个新的第三阶段提案,Object.hasOwn,它解决了Object.prototype.hasOwnProperty的一些问题。 - Sebastian Simon
32个回答

1704

2022 更新

Object.hasOwn()

Object.hasOwn() 推荐使用,而不是 Object.hasOwnProperty()。因为它适用于使用 Object.create(null) 创建的对象和已覆盖继承的 hasOwnProperty() 方法的对象。虽然可以通过在外部对象上调用 Object.prototype.hasOwnProperty() 来解决这些问题,但 Object.hasOwn() 更加直观。

示例

const object1 = {
  prop: 'exists'
};

console.log(Object.hasOwn(object1, 'prop'));
// expected output: true

原始回答

我对已经给出的答案感到非常困惑——其中大部分都是完全错误的。当然,你可以拥有具有未定义、null或false值的对象属性。因此,简单地将属性检查减少到typeof this [property]或者更糟糕的x.key会给出完全误导性的结果。

这要取决于你要找什么。如果你想知道一个对象是否实际上包含一个属性(而且它不是来自原型链上的某个地方),那么object.hasOwnProperty就是正确的方法。所有现代浏览器都支持它。(旧版本的Safari中缺少它——2.0.1及更早版本——但这些浏览器版本已经很少使用了。)

如果你要查找的是一个对象是否具有可迭代的属性(当你迭代对象的属性时,它会出现),那么做:prop in object会给你所需的效果。

由于使用hasOwnProperty可能是你想要的,而且考虑到你可能需要一种备用方法,我向你呈现以下解决方案:

var obj = {
    a: undefined,
    b: null,
    c: false
};

// a, b, c all found
for ( var prop in obj ) {
    document.writeln( "Object1: " + prop );
}

function Class(){
    this.a = undefined;
    this.b = null;
    this.c = false;
}

Class.prototype = {
    a: undefined,
    b: true,
    c: true,
    d: true,
    e: true
};

var obj2 = new Class();

// a, b, c, d, e found
for ( var prop in obj2 ) {
    document.writeln( "Object2: " + prop );
}

function hasOwnProperty(obj, prop) {
    var proto = obj.__proto__ || obj.constructor.prototype;
    return (prop in obj) &&
        (!(prop in proto) || proto[prop] !== obj[prop]);
}

if ( Object.prototype.hasOwnProperty ) {
    var hasOwnProperty = function(obj, prop) {
        return obj.hasOwnProperty(prop);
    }
}

// a, b, c found in modern browsers
// b, c found in Safari 2.0.1 and older
for ( var prop in obj2 ) {
    if ( hasOwnProperty(obj2, prop) ) {
        document.writeln( "Object2 w/ hasOwn: " + prop );
    }
}
以上是一个可以跨浏览器工作的hasOwnProperty()解决方案,但有一个注意点:它无法区分属性同时位于原型和实例上的情况 - 它只是假设它来自原型。您可以根据自己的情况将其调整为更为宽松或严格,但至少这应该更有帮助。

5
如果您正在开发一个简单的滑块插件并想检查选项项的存在,则可能确实不需要这么多。您只需像 var w = opts.w || 100; 这样做即可。但如果您正在开发一个类库之类的东西,可能需要在某些地方深入一些。 - Halil Özgür
2
@Kasztan,__proto__是非标准属性,在一些旧浏览器中不起作用。即使最近加入了Object.getPrototypeOf,标准仍然规定您不能更改现有对象的原型。 - Matt Browne
1
John Resig的答案似乎在IE8中无法正常工作,请参见此实时演示http://jsbin.com/tovaburefeva/1/edit?js,output。我认为这是因为“hasOwnProperty()”在Internet Explorer 8及以下版本上不支持主机对象,参见http://msdn.microsoft.com/en-us/library/ie/328kyd6z(v=vs.94).aspx以及https://dev59.com/FGsz5IYBdhLWcg3wBzfi。 - Adriano
16
for(prop in object) 循环只遍历可枚举属性。然而,prop in object 检查 object 在原型链中是否有属性 prop,不管它是否可枚举。 - Oriol
1
在2022年,您可以使用Object.hasOwn()。他似乎不再活跃了,但是任何寻找JavaScript书籍的人,我认为这个家伙写得最好。我想他使用Manning作为出版商,这是一家我非常喜欢的公司。 - JΛYDΞV
显示剩余7条评论

323
使用Underscore.js或者(更好的是Lodash
_.has(x, 'key');

这个调用 Object.prototype.hasOwnProperty,但是 (a) 打字更短,而且 (b) 使用 "一个安全引用 hasOwnProperty" (即使 hasOwnProperty 被覆盖也能正常工作)。

特别地,Lodash 将 _.has 定义为:

function has(object, key) {
  return object ? hasOwnProperty.call(object, key) : false;
}
// hasOwnProperty = Object.prototype.hasOwnProperty

2023年更新

正如约翰·雷西格(John Resig)的回答中所指出的那样,现在基本上已经内置了Object.hasOwn。Lodash/underscore仍然是在Object.hasOwn不可用的情况下的备选方案。


73
我猜这是因为即使问题涉及复杂的DOM操作,答案是“使用jQuery”,“添加此库”也很少成为受欢迎的解决方案。 - Winfield Trail
13
我理解你的观点,@sudowned,谢谢。顺便说一下,如果有人不想包含整个lodash库,可以编译子组件或者'npm install lodash.has',这将暴露一个仅有“has”函数的npm模块,压缩后只有175字节。另外,查看lodash.has/index.js也很有启发性,可以了解一个非常流行和值得信赖的库是如何工作的。 - Brian M. Hunt
10
“lodash”的版本可用于此项操作:.has(undefined, 'someKey') => false,而“underscore”会返回“undefined”。 - Brad Parks
21
对于所有抱怨添加lodash作为"又一个"依赖项的人:这是一种相当常见(如果不是最常见)用于此类问题的库。尽情地重新发明轮子吧。 - Priidu Neemre
19
即使你想重新发明轮子,查看现有的轮子也不是一个坏主意。 - AturSams
显示剩余6条评论

219

你可以使用这个(但请阅读下面的警告):

var x = {
  'key': 1
};

if ('key' in x) {
  console.log('has');
}

但是要注意:即使x是一个空对象,'constructor' in x也会返回true - 同样适用于'toString' in x和许多其他情况。最好使用Object.hasOwn(x, 'key')


19
请注意,它只适用于狭义上的“对象”,即声明为{}或使用构造函数创建的对象,并不接受数组或基本类型。虽然OP没有要求,但其他一些答案提供了更广泛的技术(可处理数组、字符串等)。 - Danubian Sailor
1
@РСТȢѸФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ 感谢您指出这一点(被接受的答案没有详细说明为什么应该使用in运算符或不使用。还要注意,in运算符在浏览器上有很好的支持,包括IE 5.5+,Chrome 1.0+,Firefox 1.0+,Safari 3.0+)。https://dev59.com/pXE85IYBdhLWcg3wJQCt - Adriano
4
运算符 in 也会检查原型属性,而 hasOwnProperty 只迭代用户定义的属性。参考文献:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in - adi518
3
x 中的 'key' in x 可以用于数组。证据:http://stackoverflow.com/questions/33592385/if-0-in-array-is-this-even-legal - CosmoMyzrailGorynych

146

注意:由于严格模式和 hasOwnProperty 的引入,以下内容现在已经基本过时。正确的解决方案是使用严格模式,并使用 obj.hasOwnProperty 检查属性是否存在。这个答案 比这两个东西(至少在被广泛实现之前)更早。请将此作为历史说明。


请注意,如果您没有使用严格模式,undefined 不幸地不是 JavaScript 中的保留字。因此,某人(显然是其他人)可能会想到重新定义它,从而破坏您的代码。

因此,一个更可靠的方法是:

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

相反,这种方法更冗长且较慢。 :-/

一个常见的替代方法是确保 undefined 实际上是未定义的,例如通过将代码放入一个接受额外参数(名为undefined)的函数中。要确保它不被传递值,您可以立即自己调用它,例如:

(function (undefined) {
    … your code …
    if (x.attribute !== undefined)
        … mode code …
})();

6
因为void 0被定义为返回标准的“undefined”,所以在代码中可以这样写:x.attribute !== void 0。这个表达式的意思是当x.attribute不等于“undefined”时,条件成立。 - Brian M. Hunt
1
Brian:我不是专家,但那似乎是一个聪明的方法来做对。 - Christopher Smith
42
如果这位著名的“某人”重新定义了未定义的含义,我认为最好的做法是重写那段代码。 - Oskar Holmkratz
3
要使未定义的变量稳定可靠,最好是在闭包内工作,并具有不匹配的函数签名:(function (undefined) { // undefined is actually undefined here })(); - bgusach
1
@evanrmurphy 不要使用那个,它已经严重过时了(请参见我的回答开头的注释)。 - Konrad Rudolph
显示剩余3条评论

61
if (x.key !== undefined)

Armin Ronacher 似乎已经 比我更快地完成了这件事, 但是:

Object.prototype.hasOwnProperty = function(property) {
    return this[property] !== undefined;
};

x = {'key': 1};

if (x.hasOwnProperty('key')) {
    alert('have key!');
}

if (!x.hasOwnProperty('bar')) {
    alert('no bar!');
}

根据Konrad RudolphArmin Ronacher的指出,一种更加安全但速度较慢的解决方案是:

链接

Object.prototype.hasOwnProperty = function(property) {
    return typeof this[property] !== 'undefined';
};

2
我认为这还不够好。x.hasOwnProperty('toString') === true; - Joe Simmons
不是要求反对,而是要理解。除了布尔值true或false之外,x.hasOwnProperty是否会返回任何其他值?如果不是的话,按照发布的代码应该每次都能正常工作。我想可能会被重写,但是,除非你知道重写方法,否则依赖结果将永远不可靠。 - enobrev
1
我认为我们存在误解。我的意思是,使用你的方法,它会说'toString'是它自己的属性,但实际上并不是。 - Joe Simmons
3
Object.prototype 已经有了一个内置的、正确的 hasOwnProperty() 方法。覆盖它并使用一个不正确的实现是非常糟糕的想法(因为 1. 属性可以有值 undefined,2. 这样做会给继承属性带来错误的判断)。不正确的答案应该被删除。我不知道在2008年9月(您看到Resig的回答时)是否可以这样做,所以建议现在这样做。 - T.J. Crowder

44

考虑以下 Javascript 对象:

const x = {key: 1};
您可以使用in运算符检查对象上是否存在属性:
console.log("key" in x);

你也可以使用 for - in 循环遍历对象的所有属性,然后检查特定的属性:

for (const prop in x) {
    if (prop === "key") {
        //Do something
    }
}

您必须考虑此对象属性是否可枚举,因为不可枚举的属性不会在 for-in 循环中显示。另外,如果可枚举的属性遮蔽了原型的非枚举属性,则它不会出现在 Internet Explorer 8 及更早版本中。

如果您想要所有实例属性的列表,无论是否可枚举,可以使用

Object.getOwnPropertyNames(x);

这将返回一个对象上存在的所有属性名称的数组。

反射提供了可用于与 JavaScript 对象交互的方法。静态的 Reflect.has() 方法作为函数类似于 in 运算符。

console.log(Reflect.has(x, 'key'));
// expected output: true

console.log(Reflect.has(x, 'key2'));
// expected output: false

console.log(Reflect.has(object1, 'toString'));
// expected output: true

最后,你可以使用 typeof 操作符直接检查对象属性的数据类型:

if (typeof x.key === "undefined") {
    console.log("undefined");
}
如果对象上不存在该属性,则返回字符串 undefined。否则,它将返回适当的属性类型。但是请注意,这并不总是检查对象是否具有属性的有效方法,因为您可以将属性设置为undefined,在这种情况下,使用typeof x.key仍将返回true(即使该键仍然在对象中)。 类似地,您可以通过直接与 Javascript 属性 undefined 进行比较来检查属性是否存在。
if (x.key === undefined) {
    console.log("undefined");
}

除非在 x 对象上将键值特别设置为 undefined,否则此方法应该有效。


33

让我们澄清一些混淆的问题。首先,假设hasOwnProperty已经存在;这在当前使用的绝大多数浏览器中都是正确的。

hasOwnProperty会返回一个布尔值,如果传递给它的属性名称已添加到对象中,则返回true。它完全独立于实际分配给它的值,该值可能恰好为undefined

因此:

var o = {}
o.x = undefined

var a = o.hasOwnProperty('x')  // a is true
var b = o.x === undefined // b is also true

然而:

var o = {}

var a = o.hasOwnProperty('x')  // a is now false
var b = o.x === undefined // b is still true

当原型链中的对象具有值为undefined的属性时会发生什么问题?hasOwnProperty将对其返回false,!== undefined也是如此。然而,for..in仍然会在枚举中列出它。

总之,没有跨浏览器的方法(因为Internet Explorer不公开__prototype__)来确定特定标识符未附加到对象或其原型链中的任何内容。


32

如果你正在寻找一处房产,那么"没有"是不够的。你需要:

if ('prop' in obj) { }

通常情况下,您不需要关心属性是来自原型还是对象。

然而,因为您在示例代码中使用了“key”,看起来您把对象当作哈希表来处理,这时你的回答就有意义了。所有哈希键都将成为对象的属性,您可以避免原型贡献的额外属性。

John Resig的回答非常全面,但我认为它并不清晰,特别是在何时使用“'prop' in obj”方面。


1
请注意,in运算符在浏览器上有很好的支持,包括IE 5.5+,Chrome 1.0+,Firefox 1.0+,Safari 3.0+。https://dev59.com/pXE85IYBdhLWcg3wJQCt - Adriano
正如另一条评论中提到使用 in 运算符:"它仅适用于 '对象' 的狭义定义,因此必须声明为 {} 或使用构造函数创建,而不接受数组或基元类型。虽然 OP 没有要求,但其他答案提供了更广泛的技术(适用于数组、字符串等)"。 - Adriano
1
评论一下,因为我已经被两次没有评论的投票打败了。但我仍然喜欢我的答案。也许那些这样做的人想要一个“全面”的答案来测试所有类型的属性。但我的答案是概念性的,简洁明了。关于Adrien Be所说的不可枚举属性是指那些不适用于一般用户范围的属性,因此从概念上讲,“in”是可以的 ;) - Gerard ONeill

25

用以下代码测试简单对象:

if (obj[x] !== undefined)

如果你不知道对象的类型,使用:

if (obj.hasOwnProperty(x))

所有其他选项的速度都比较慢...

细节

对于在Node.js下执行100,000,000次的性能评估,针对这里提出的五个选项:

function hasKey1(k,o) { return (x in obj); }
function hasKey2(k,o) { return (obj[x]); }
function hasKey3(k,o) { return (obj[x] !== undefined); }
function hasKey4(k,o) { return (typeof(obj[x]) !== 'undefined'); }
function hasKey5(k,o) { return (obj.hasOwnProperty(x)); }

评估告诉我们,除非我们特别想检查对象本身以及其原型链,否则我们不应该使用常见的形式

if (X in Obj)...

根据使用情况,它的速度会变慢2到6倍

hasKey1 execution time: 4.51 s
hasKey2 execution time: 0.90 s
hasKey3 execution time: 0.76 s
hasKey4 execution time: 0.93 s
hasKey5 execution time: 2.15 s

简而言之,如果您的 Obj 不是一个简单对象,并且希望避免检查对象的原型链以确保 x 直接归属于 Obj,则使用 if (obj.hasOwnProperty(x))...

否则,当使用简单对象且不担心对象的原型链时,使用 if (typeof(obj[x]) !== 'undefined')... 是最安全和最快捷的方式。

如果您将简单对象用作哈希表并且从未进行任何奇怪的操作,则我会使用 if (obj[x])...,因为我认为它更易读。


你的中间段暗示着“IF (x in obj)”适用于检查原型链,但是你倒数第二段又暗示它检查链并且速度更快。那么,“语法”是使用(x in obj)的唯一原因吗? - Gerard ONeill
"在 obj 中的 x" 是唯一会检查原型链的被评估选项。 - davidhadas

20

是的,可以使用 Object.prototype.hasOwnProperty.call(x, 'key') ,如果 x 有一个名为 hasOwnProperty 的属性也可以正常工作 :)

但是这个方法只能检查是否为自有属性。如果你想检查是否具有可能被继承的属性,可以使用 typeof x.foo != 'undefined'


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接