【问题标题】:Checking 'undefined' or 'null' of any Object检查任何对象的“未定义”或“空”
【发布时间】:2025-12-06 05:05:03
【问题描述】:

我正在从事 Angular 项目,并且不时地检查 undefinednull 对象或其属性。通常我使用 lodash _.isUndefined() 见下面的例子:

this.selectedItem.filter(i => {
    if(_.isUndefined(i.id)) {
      this.selectedItem.pop();
    }
})

我看不出有什么问题。但在审查上述代码时,我与我的同事进行了讨论。他告诉我,如果iif 语句之前得到undefined,那么它将抛出异常。相反,他建议我总是像这样检查ii.id

if(!!i && !!i.id) {
      this.selectedItem.pop();
}

我确信他想说的话与他在上面的代码中检查undefined 的方式不同。但后来我在想 lodash _.isUndefined 的目的是什么?

谁能告诉我什么是最好或干净的方法。因为对我来说!!i && !!i.id 根本不可读。

非常感谢。

【问题讨论】:

  • 在您的 Angular 项目中,您使用的是打字稿还是仅使用 javascript?
  • if(!!i && !!i.id)_.isUndefined 进行不同检查!!!它将检查属性是否真实,而不是undefined。例如,它还将捕获null0""(空字符串)。此外,第一次隐式布尔转换 !!i 是多余的。
  • @Anouar 是的,我正在使用 TS
  • 我通常使用 if (!variable)。那么如果变量是字符串并且等于“”或者是数字并且等于0,则条件为真

标签: javascript angular lodash


【解决方案1】:

您可以使用_.isNil() 来检测undefinednull。由于您使用的是Array.filter(),因此您希望返回!_.isNil() 的结果。由于i应该是一个对象,你可以使用!_.isNil(i && i.id)

注意:您将Array.filter() 用作Array.forEach()Array.filter() 的回调应该返回一个布尔值,过滤的结果是一个新的数组。

const selectedItem = [
  undefined,
  {},
  { id: 5 },
  undefined,
  { id: 7 },
];

const result = selectedItem.filter(i => !_.isNil(i?.id));

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

也可以使用_.reject(),省去加!的需要:

const selectedItem = [
  undefined,
  {},
  { id: 5 },
  undefined,
  { id: 7 },
];

const result = _.reject(selectedItem, i => _.isNil(i?.id));

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

【讨论】:

    【解决方案2】:

    使用typeof i.id === 'undefined' 检查undefined,使用i.id === null 检查null

    您可以编写自己的辅助函数来包装任何逻辑,如 LoDash 所具有的。 !!i && !!i.id 的条件只是寻找虚假值(空字符串、0 等),而不仅仅是 nullundefined

    【讨论】:

    • 我曾建议在 cmets 中使用 NaN,例如这一愚蠢的事情。 typeof(NaN) -> 返回"number"
    • @tutorialfeed 符合您的标准。
    【解决方案3】:

    引用具有undefined 的变量,因为它的值不会引发任何错误。你会得到一个ReferenceError 来引用未定义的变量:

    > i
    Uncaught ReferenceError: i is not defined
    

    如果您将未定义的变量传递给函数,则会抛出 ReferenceError 并且不会执行该函数。

    > _.isUndefined(i)
    Uncaught ReferenceError: i is not defined
    

    typeof 运算符应该用于安全地检查变量是否已定义:

    > typeof i
    'undefined'
    

    在您的代码中,i 已定义(它是一个函数参数),因此通过引用它您不会得到任何 ReferenceError。当 i 被定义时,代码将抛出 TypeError具有 undefined,并且您将其视为对象:

    > var i = undefined; i.id
    Uncaught TypeError: Cannot read property 'id' of undefined
    

    【讨论】:

    • 直接从源头回答:P
    • @vlaz 是的,这很正式 :)
    • 是的,我在控制台中进行了此检查,但我的目的是编写更简洁易读的代码。 :)
    【解决方案4】:

    你可以检查i,如果它不是真的,或者属性是undefinednull,然后做点什么。

    if (!i || i.id === undefined || i.id === null) {
        this.selectedItem.pop();
    }
    

    【讨论】:

    • 是的。这是正确的答案。 (我正要写完全一样的东西)
    【解决方案5】:

    你的朋友是对的。当你这样做时,_.isUndefined(i.id) 你假设i 不是undefined。您假设 i 是一个对象,它将具有 id 属性,您正在检查它是否为 falsey

    当变量i 本身是undefined 时会发生什么?所以你最终会得到undefined.id,这是一个错误。因此,您可以简单地这样做

    if(i && i.id) {
      // You're good to go
    }
    

    上面将检查所有 falsey 值,包括 0""。因此,如果您想非常具体,则必须使用 typeof 运算符检查两者的类型。

    【讨论】:

    • 除非i.id 为零。或者如果它是一个空字符串。两者都可能不应该发生,但它可能与一些模拟数据或一些合成记录一起发生。
    【解决方案6】:

    我建议检查if(typeof(element) == "number" && element) {} 在这种情况下typeof() 部分捕获任何非数字,element 部分应该捕获任何NaNs(typeof(NaN) 返回“数字”)

    【讨论】:

      【解决方案7】:

      let words = [null, undefined, 'cheaters', 'pan', 'ear', 'era']
      
      console.log(words.filter(word => word != null));

      【讨论】:

        【解决方案8】:

        您可以使用lodash#get(如果根值是空值或未定义,它将处理),然后使用==!= 将输出与空值进行比较,而不是使用===!==。如果输出为nullundefined,则与== 比较为真,!= 为假。

        const selectedItem = [
          undefined,
          {},
          {id: null},
          { id: 5 },
          undefined,
          { id: 0 },
          { id: 7 },
        ];
        
        const res = selectedItem.filter(a => _.get(a, 'id') != null);
        
        console.log(res);
        <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

        使用lodash#get,您可以检查任何嵌套级别是否存在,而无需后续&&,如a && a.b && a.b.c && a.b.c.d(如果是d)查看this answer of mine,使用lodash#get进行嵌套级别检查

        您也可以使用_.isNill 而不是与null 比较将==!=

        【讨论】:

          【解决方案9】:

          另一种更“笨拙”的方法是使用_.conforms。我认为可读性要好得多,您可以直接访问id,因此在此之前未定义没有问题:

          const items = [
            undefined,
            { id: null},
            { id: 5 },
            { id: "4" },
            { id: undefined },
            undefined,
            { id: 0 },
            { id: 7 },
            { id: () => 3 }
          ];
          
          const numbersOnly = _.filter(items, _.conforms({'id': _.isNumber}));
          console.log('numbers', numbersOnly);
          
          const allDefined = _.filter(items, _.conforms({'id': _.negate(_.isUndefined)}));
          console.log('defined', allDefined);
          
          const stringsOnly = _.filter(items, _.conforms({'id': _.isString}));
          console.log('strings', stringsOnly);
          
          const functionsOnly = _.filter(items, _.conforms({'id': _.isFunction}));
          console.log('functions', functionsOnly);
          <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

          【讨论】:

            【解决方案10】:

            Javascript 现在有(Chrome 80、Safari 13.4)Optional chaining (?.)

            可选的链接运算符 (?.) 允许读取 a 的值 位于连接对象链深处的属性,没有 必须明确验证链中的每个引用都是 有效。

            这意味着如果 i 未定义,您可以检查 id 而不会导致异常

            this.selectedItem.filter(i => {
              if(i?.id) {
                this.selectedItem.pop();
              }
            })
            

            或者由于您使用过滤器,您可以检查test this live on the filter documentation

            const words = ['spray', undefined, 'elite', 'exuberant', 'destruction', 'present'];
            
            const result = words.filter(word => word?.length > 6);
            
            console.log(result);
            // expected output: Array ["exuberant", "destruction", "present"]
            

            另外,还值得一提的是,作为@Koushik Chatterjee 的回答点,lodash _.get 允许您安全地描述到深层属性的路径,甚至在它不存在的情况下给出默认值。

            var object = { 'a': [{ 'b': { 'c': 3 } }] };
            _.get(object, ['a', '0', 'b', 'c']);
            // => 3
            
            _.get(object, 'a.b.c', 'default');
            // => 'default'
            

            【讨论】: