typeof foo['bar'] !== 'undefined' 和 'bar' in foo 的区别是什么?

5
这两条表达式的返回值有何不同呢?假设以下条件都得到满足:
1. foo 是一个对象, 2. foo 中没有属性的值被明确设置为 undefined
表达式 1:typeof foo['bar'] !== 'undefined'。 表达式 2:'bar' in foo
其中,表达式 1 的返回值为 true 或 false,表示在 foo 对象中是否存在 bar 属性。 而表达式 2 的返回值则只能是 true 或 false,表示在 foo 对象中是否存在名为 bar 的属性,无法判断该属性的值是否为 undefined。

foo.hasOwnProperty('bar') 怎么样? - gen_Eric
1
@Rocket 不,那不是同一件事情 - 属性访问器表达式还会检查原型链。 - Pointy
@Pointy 还有 in 操作符,是吧? - Šime Vidas
1
@Šime Vidas - 是的,[[HasProperty]] 是基于 [[GetProperty]] 而非 [[GetOwnProperty]] 定义的。 - Pointy
1
此外,我认为将属性视为具有显式的“未定义”值是没有意义的——如果对象具有属性,则其值至少为null,但不是“未定义”。 - Pointy
1
@Pointy是的,我不关心那些被设置为“undefined”的属性。但是我必须在那里加入条件2,否则我就会被充斥着指出,如果一个属性被设置为“undefined”,表达式1返回“false”,而表达式2则返回“true”的答案。 - Šime Vidas
6个回答

5

第一个测试 foo 中的 bar 的值。

第二个测试 foo 是否存在 bar 属性。

var foo = {bar:undefined};

typeof foo['bar'] !== 'undefined'; // false

'bar' in foo;  // true

编辑:

根据下方的评论,OP遇到的问题是访问window.documentdomConfig属性会抛出错误。

这个问题与typeof运算符无关,而是一个特定于Firefox的问题。

这个问题在2003年记录在这里作为一个错误被记录。

该报告中的一些值得注意的评论:

Zbigniew Braniecki [:gandalf] 2003-11-19 09:09:31 PST

那为什么可以用for-in迭代呢?

Boris Zbarsky (:bz) 2003-11-19 09:24:05 PST

因为它在nsIDOM3Document接口上被定义为一个属性。只是如果你尝试访问它的getter就会抛出异常......

Zbigniew Braniecki [:gandalf] 2003-11-19 09:33:53 PST

......那么这是什么样的bug?

目标是从接口中删除未实现的方法/属性,还是实现它?!?

Boris Zbarsky (:bz) 2003-11-19 09:53:23 PST

目标是最终实现它。


1
@patrick,我更新了我的问题。我关心这两个表达式的返回值... - Šime Vidas
1
@patrick 我问题的第二个条件是 foo 不包含任何明确设置为 undefined 值的属性。 - Šime Vidas
@Šime Vidas:您是否遇到过特定的问题?根据条件2,结果应该是相同的。 - user113716
2
@Šime Vidas:这里是一个2003年产生的错误报告 - user113716
2
@patrick 7年前的评论:“目标是最终实现这一点。”他们确实花了很长时间。:) - Šime Vidas
显示剩余6条评论

2
根据规范,我的理解是它们应该是相同的。 "in"运算符语义是通过调用内部(概念性)[[HasProperty]]方法来定义的,该方法本身是基于[[GetProperty]]定义的。当[[HasProperty]]返回"undefined"时,"in"运算符的结果为布尔值false;否则为true。根据[[GetProperty]]的定义,这意味着属性访问的"undefined"结果具有相同的含义。请注意保留HTML标签。

1
var foo = {};
foo.bar = undefined;

console.log("bar" in foo); // true
console.log(typeof foo["bar"] !== "undefined"); // false

var Con = function() {};
Con.prototype.bar = undefined;
var foo = new Con;

console.log("bar" in foo); // true
console.log(typeof foo["bar"] !== "undefined"); // false

in检查与使用for in循环并在for in循环中有key时返回true是相同的。

[编辑]没有看到您的“不明确将其设置为undefined”条件。

var foo = {}
Object.defineProperty(foo, "bar", { 
    "value": 42,
    "enumerable": false

});

console.log("bar" in foo); // true (in chrome & FF4)
console.log(typeof foo["bar"] !== 'undefined'); // true

我其实预期如果将in测试设置为非枚举,它会失败。看起来它们是相同的。

var foo = {}
Object.defineProperty(foo, "bar", { 
    "value": 42,
    "writable": false

});

console.log("bar" in foo); // true (in chrome & FF4)
console.log(typeof foo["bar"] !== 'undefined'); // true

如果属性是“只读”,那么这两个测试仍然有效。

var foz = {}
Object.defineProperty(foz, "bar", { 
    "value": 42
});
var foo = Object.freeze(foz);

console.log("bar" in foo); // true (in chrome & FF4)
console.log(typeof foo["bar"] !== 'undefined'); // true

冻结对象也不会破坏测试。


1

根据您所概述的条件,两者没有区别。它们都应该产生相同的布尔结果,并且在原型查找方面的行为应该是相同的。

Object.prototype.hasOwnProperty.call(null, foo, 'bar')

是另一个常见的习语,与这两个相同,但不包括仅在原型上可用的属性。


1

在这两个条件下,这些表达式应该给你相同的结果,除非

  • foobar 属性定义了一个 EcmaScript 5 getter。由于第一个表达式实际上读取属性值,因此将调用该 getter。第二个表达式仅检查属性是否存在,因此不需要调用任何此类 getter。
  • 你的目标是 Internet Explorer 4.0 - in 运算符直到 JavaScript 1.4 才被添加 ;)

这里有一些示例代码,以说明支持浏览器(Chrome、IE9、FF4)之间的差异:

var foo = {};
Object.defineProperty(foo, "bar", { 
    "get": function () {document.write("Getter invoked!<br/>"); return foo;}
});

document.write('"bar" in foo -->' + 
               ("bar" in foo));
document.write('<br/>');
document.write('typeof foo["bar"] !== "undefined" -->' +
               (typeof foo["bar"] !== "undefined"));
document.write('<br/>');

规范中说明,“in”操作符将调用[[HasProperty]],而[[HasProperty]]被定义为调用[[GetProperty]]。那么这不会同时调用getter吗? - Pointy
@Pointy 我必须承认我没有详细研究规范,但观察到了Chrome、FF4和IE9之间的差异。 - Martin
嗯,这确实是一个有趣的问题;这是语言中我从未完全确定的奇怪方面之一。最好尽可能避免这种情况 :-) - Pointy
1
@Pointy:不,[[GetProperty]] 不会执行 getter,它只是基于属性通过 [[GetOwnProperty]] 构建一个 Property Descriptor - Christian C. Salvadó
啊,好的,现在我明白那是什么意思了。谢谢@CMS!! - Pointy

0

第一个首先检查 foo 中是否存在 'bar' 键(如果未找到,则返回 undefined),然后检查该键的类型。 第二个仅检查存在性。


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