检测未定义的对象属性

检查 JavaScript 中的对象属性是否undefined的最佳方法是什么?

答案

采用:

if (typeof something === "undefined") {
    alert("something is undefined");
}

如果对象变量具有某些属性,则可以使用类似的方法:

if (typeof my_obj.someproperties === "undefined"){
    console.log('the property is not available...'); // print into console
}

我相信对此主题有许多不正确的答案。与通常的看法相反,“未定义” 不是 JavaScript 中的关键字,实际上可以为其分配值。

正确的代码

执行此测试的最可靠方法是:

if (typeof myVar === "undefined")

这将始终返回正确的结果,甚至可以处理未声明myVar的情况。

简并代码。不使用。

var undefined = false;  // Shockingly, this is completely legal!
if (myVar === undefined) {
    alert("You have been misled. Run away!");
}

此外,在未声明 myVar 的情况下, myVar === undefined将引发错误。

尽管这里的许多其他答案都强烈建议typeoftypeof 是一个不好的选择 。永远不要将其用于检查变量是否具有值undefined ,因为它可以作为对值undefined和变量是否存在的组合检查。在大多数情况下,您知道什么时候存在变量,如果您在变量名称或字符串文字'undefined'中输入错误, typeof只会导致潜在的失败。

var snapshot = …;

if (typeof snaposhot === 'undefined') {
    //         ^
    // misspelled¹ – this will never run, but it won’t throw an error!
}
var foo = …;

if (typeof foo === 'undefned') {
    //                   ^
    // misspelled – this will never run, but it won’t throw an error!
}

因此,除非您要进行特征检测 ²,否则不确定给定名称是否会在范围内(例如检查typeof module !== 'undefined'作为特定于 CommonJS 环境的代码中的步骤), typeof是有害的选择在变量上,正确的选择是直接比较值:

var foo = …;

if (foo === undefined) {
    ⋮
}

关于此的一些常见误解包括:

  • 读取 “未初始化的” 变量( var foo )或参数( function bar(foo) { … } ,称为bar() )将失败。这根本不是真的–没有显式初始化的变量和没有给定值的参数始终变为undefined ,并且始终在范围内。

  • undefined可以被覆盖。还有很多。 undefined不是 JavaScript 中的关键字。相反,它是具有 Undefined 值的全局对象的属性。但是,从 ES5 开始,此属性为只读不可配置 。没有现代的浏览器将允许更改undefined属性,从 2017 年开始,这种情况已经存在很长时间了。缺乏严格模式也不会影响undefined的行为 - 只会使undefined = 5类的语句undefined = 5而不是抛出。但是,由于它不是关键字,因此可以使用undefined名称声明变量,并且可以更改这些变量,从而形成以下常见的模式:

    (function (undefined) {
        // …
    })()

    比使用全局undefined 危险。如果必须与 ES3 兼容,请将undefined替换为void 0 –请勿使用typeof 。 ( void一直是一元运算符,其任何操作数的计算结果均为未定义值。)

随着变量如何发挥作用,是时候解决一个实际问题:对象属性。没有理由将typeof用于对象属性。较早的关于特征检测的例外不适用于此处typeof仅对变量具有特殊的行为,而引用对象属性的表达式不是变量。

这个:

if (typeof foo.bar === 'undefined') {
    ⋮
}

始终完全等于此 ³:

if (foo.bar === undefined) {
    ⋮
}

并考虑到以上建议,以避免使读者对您为什么使用typeof感到困惑,因为使用===来检查相等性是最有意义的,因为以后可以将其重构为检查变量的值,并且因为它看起来比较好看,所以您也应该在此处始终使用=== undefined

关于对象属性,还需要考虑的其他事情是您是否真的要检查undefined的对象。给定的属性名称可以不存在于对象上(在读取时产生undefined的值),可以在对象本身上显示为undefined值,可以在对象的原型上显示为undefined值,也可以在任何一个具有undefined的属性上出现值。 'key' in obj会告诉您密钥是否在对象原型链的任何位置,而Object.prototype.hasOwnProperty.call(obj, 'key')会告诉您Object.prototype.hasOwnProperty.call(obj, 'key')是否直接在对象上。不过,我不会在这个答案中详细介绍关于原型以及将对象用作字符串键映射的原因,因为它主要是为了抵消其他答案中的所有不良建议,而与原始问题的可能解释无关。阅读有关 MDN 的对象原型的更多信息!

¹ 示例变量名称的异常选择?这是来自 Firefox 的 NoScript 扩展的真正的死代码。
² 不过,不要以为总体上不知道范围是可以的。动态范围滥用引起的额外漏洞: 零项目 1225
³ 再次假定为 ES5 + 环境,并且undefined是指全局对象的undefined属性。否则用void 0代替。

在 JavaScript 中,为null ,没有定义 。它们具有不同的含义。

  • 未定义表示尚未定义变量值;目前尚不清楚该值是多少。
  • null表示已定义变量值并将其设置为 null(无值)。

Marijn Haverbeke 在他的免费在线书 “ Eloquent JavaScript ”(强调我的)中指出:

还有一个相似的值 null,其含义是 “已定义此值,但没有值”。 undefined 和 null 之间的含义差异主要是学术上的,通常不是很有趣。 在实际程序中,通常有必要检查某些东西是否 “有价值”。在这些情况下,可以使用表达式 something == undefined,因为即使它们的值不完全相同,null == undefined 也会产生 true。

因此,我认为检查某些内容是否未定义的最佳方法是:

if (something == undefined)

希望这可以帮助!

编辑:响应您的编辑,对象属性应该以相同的方式工作。

var person = {
    name: "John",
    age: 28,
    sex: "male"
};

alert(person.name); // "John"
alert(person.fakeVariable); // undefined

这是什么意思: “未定义的对象属性”

实际上,这可能意味着两个完全不同的事物!首先,它可以表示从未在对象中定义的属性,其次,它可以表示具有未定义值属性 。让我们看一下这段代码:

var o = { a: undefined }

oa是未定义的吗?是! 其值是不确定的。 ob是 undefined 吗?当然!根本没有属性 “b”!好的,现在看一下两种情况下不同方法的行为:

typeof o.a == 'undefined' // true
typeof o.b == 'undefined' // true
o.a === undefined // true
o.b === undefined // true
'a' in o // true
'b' in o // false

我们可以清楚地看到typeof obj.prop == 'undefined'obj.prop === undefined是等效的,并且它们不能区分那些不同的情况。而'prop' in obj可以检测到根本没有定义属性并且不注意可能未定义的属性值的情况。

那么该怎么办?

1)您想知道一个属性是否未按第一个含义或第二个含义定义(最典型的情况)。

obj.prop === undefined // IMHO, see "final fight" below

2)您只想知道对象是否具有某些属性,而不关心其值。

'prop' in obj

笔记:

  • 您不能同时检查一个对象及其属性。例如,此xa === undefined或此类型的typeof xa == 'undefined'引发ReferenceError: x is not defined如果typeof xa == 'undefined' ReferenceError: x is not defined x。
  • 变量undefined是一个全局变量(因此实际上它是浏览器中的window.undefined )。因为 ECMAScript 的第 1 版已支持,因为 ECMAScript 的 5 它是只读的 。因此,在现代浏览器中,不能像许多作者喜欢的那样将其重新定义为 true ,但是对于较旧的浏览器来说,这仍然是正确的。

最后一战: obj.prop === undefined vs typeof obj.prop == 'undefined'

obj.prop === undefined

  • 有点短,看起来更漂亮
  • 如果您拼错了undefined ,JavaScript 引擎会给您一个错误

obj.prop === undefined

  • undefined可以在旧的浏览器中覆盖

typeof obj.prop == 'undefined'

  • 真的很普遍!它可以在新旧浏览器中使用。

typeof obj.prop == 'undefined'

  • 'undefned'拼写错误 )只是一个字符串常量,因此,如果您像我刚才那样拼写错误,那么 JavaScript 引擎将无法为您提供帮助。

更新(用于服务器端 JavaScript):

Node.js 支持将undefinedglobal.undefined (也可以不使用'global' 前缀使用)。我不知道服务器端 JavaScript 的其他实现。

问题归结为三种情况:

  1. 对象具有属性,其值不是undefined
  2. 该对象具有属性,其值是undefined
  3. 该对象不具有该属性。

这告诉我们一些我认为重要的事情:

未定义成员与具有未定义值的已定义成员之间存在差异。

但是typeof obj.foo是, typeof obj.foo并没有告诉我们这三种情况中的哪一种。但是,我们可以将它与"foo" in obj结合使用以区分大小写。

|  typeof obj.x === 'undefined' | !("x" in obj)
1.                     { x:1 } |  false                        | false
2.    { x : (function(){})() } |  true                         | false
3.                          {} |  true                         | true

值得注意的是,对于null条目,这些测试也相同

|  typeof obj.x === 'undefined' | !("x" in obj)
                    { x:null } |  false                        | false

我认为在某些情况下,检查属性是否存在比检查属性是否未定义更有意义(更清楚),并且唯一的区别在于情况 2,这种情况很少见。对象中具有未定义值的实际条目。

例如:我刚刚重构了一堆代码,这些代码进行了一堆检查对象是否具有给定的属性。

if( typeof blob.x != 'undefined' ) {  fn(blob.x); }

不用检查未定义就可以更清楚地看到。

if( "x" in blob ) { fn(blob.x); }

但是如上所述,它们并不完全相同(但足以满足我的需求)。

if ( typeof( something ) == "undefined")

这对我有用,而其他人则没有。

我不确定在typeof中使用===的起源是什么,并且按照惯例,我看到它在许多库中都使用过,但是 typeof 运算符返回的是字符串文字,而且我们很早就知道,所以为什么要这么做也想输入检查吗?

typeof x;                      // some string literal "string", "object", "undefined"
if (typeof x === "string") {   // === is redundant because we already know typeof returns a string literal
if (typeof x == "string") {    // sufficient

我从相关问题中交叉回答我的答案 如何在 JavaScript 中检查 “未定义”?

特定于此问题,请参见someObject.<whatever>测试用例。


一些场景说明了各种答案的结果: http : //jsfiddle.net/drzaus/UVjM4/

(请注意,在范围内的包装器中, in测试中使用var会有所不同)

参考代码:

(function(undefined) {
    var definedButNotInitialized;
    definedAndInitialized = 3;
    someObject = {
        firstProp: "1"
        , secondProp: false
        // , undefinedProp not defined
    }
    // var notDefined;

    var tests = [
        'definedButNotInitialized in window',
        'definedAndInitialized in window',
        'someObject.firstProp in window',
        'someObject.secondProp in window',
        'someObject.undefinedProp in window',
        'notDefined in window',

        '"definedButNotInitialized" in window',
        '"definedAndInitialized" in window',
        '"someObject.firstProp" in window',
        '"someObject.secondProp" in window',
        '"someObject.undefinedProp" in window',
        '"notDefined" in window',

        'typeof definedButNotInitialized == "undefined"',
        'typeof definedButNotInitialized === typeof undefined',
        'definedButNotInitialized === undefined',
        '! definedButNotInitialized',
        '!! definedButNotInitialized',

        'typeof definedAndInitialized == "undefined"',
        'typeof definedAndInitialized === typeof undefined',
        'definedAndInitialized === undefined',
        '! definedAndInitialized',
        '!! definedAndInitialized',

        'typeof someObject.firstProp == "undefined"',
        'typeof someObject.firstProp === typeof undefined',
        'someObject.firstProp === undefined',
        '! someObject.firstProp',
        '!! someObject.firstProp',

        'typeof someObject.secondProp == "undefined"',
        'typeof someObject.secondProp === typeof undefined',
        'someObject.secondProp === undefined',
        '! someObject.secondProp',
        '!! someObject.secondProp',

        'typeof someObject.undefinedProp == "undefined"',
        'typeof someObject.undefinedProp === typeof undefined',
        'someObject.undefinedProp === undefined',
        '! someObject.undefinedProp',
        '!! someObject.undefinedProp',

        'typeof notDefined == "undefined"',
        'typeof notDefined === typeof undefined',
        'notDefined === undefined',
        '! notDefined',
        '!! notDefined'
    ];

    var output = document.getElementById('results');
    var result = '';
    for(var t in tests) {
        if( !tests.hasOwnProperty(t) ) continue; // bleh

        try {
            result = eval(tests[t]);
        } catch(ex) {
            result = 'Exception--' + ex;
        }
        console.log(tests[t], result);
        output.innerHTML += "\n" + tests[t] + ": " + result;
    }
})();

结果:

definedButNotInitialized in window: true
definedAndInitialized in window: false
someObject.firstProp in window: false
someObject.secondProp in window: false
someObject.undefinedProp in window: true
notDefined in window: Exception--ReferenceError: notDefined is not defined
"definedButNotInitialized" in window: false
"definedAndInitialized" in window: true
"someObject.firstProp" in window: false
"someObject.secondProp" in window: false
"someObject.undefinedProp" in window: false
"notDefined" in window: false
typeof definedButNotInitialized == "undefined": true
typeof definedButNotInitialized === typeof undefined: true
definedButNotInitialized === undefined: true
! definedButNotInitialized: true
!! definedButNotInitialized: false
typeof definedAndInitialized == "undefined": false
typeof definedAndInitialized === typeof undefined: false
definedAndInitialized === undefined: false
! definedAndInitialized: false
!! definedAndInitialized: true
typeof someObject.firstProp == "undefined": false
typeof someObject.firstProp === typeof undefined: false
someObject.firstProp === undefined: false
! someObject.firstProp: false
!! someObject.firstProp: true
typeof someObject.secondProp == "undefined": false
typeof someObject.secondProp === typeof undefined: false
someObject.secondProp === undefined: false
! someObject.secondProp: true
!! someObject.secondProp: false
typeof someObject.undefinedProp == "undefined": true
typeof someObject.undefinedProp === typeof undefined: true
someObject.undefinedProp === undefined: true
! someObject.undefinedProp: true
!! someObject.undefinedProp: false
typeof notDefined == "undefined": true
typeof notDefined === typeof undefined: true
notDefined === undefined: Exception--ReferenceError: notDefined is not defined
! notDefined: Exception--ReferenceError: notDefined is not defined
!! notDefined: Exception--ReferenceError: notDefined is not defined

如果你这样做

if (myvar == undefined )
{ 
    alert('var does not exists or is not initialized');
}

当变量myvar不存在时,它将失败,因为未定义 myvar,因此脚本已损坏并且测试无效。

因为窗口对象在函数外部具有全局作用域(默认对象),所以声明将 “附加” 到窗口对象。

例如:

var myvar = 'test';

全局变量myvarwindow.myvarwindow ['myvar'] 相同

为了避免在存在全局变量时测试错误,最好使用:

if(window.myvar == undefined )
{ 
    alert('var does not exists or is not initialized');
}

变量是否确实存在的问题并不重要,其值不正确。否则,用 undefined 初始化变量是愚蠢的,最好使用值 false 进行初始化。当您知道声明的所有变量都用 false 初始化时,您可以简单地检查其类型或依靠!window.myvar来检查其是否具有正确 / 有效的值。因此,即使未定义变量,对于myvar = undefinedmyvar = falsemyvar = 0 !window.myvar也是相同的。

当您需要特定类型时,请测试变量的类型。为了加快测试条件,您最好执行以下操作:

if( !window.myvar || typeof window.myvar != 'string' )
{
    alert('var does not exists or is not type of string');
}

当第一个简单条件为 true 时,解释器将跳过下一个测试。

始终最好使用变量的实例 / 对象来检查它是否具有有效值。它更稳定,是更好的编程方式。

(y)