【问题标题】:How to determine whether an object has a given property in JavaScriptJavaScript中如何判断一个对象是否具有给定的属性
【发布时间】:2010-12-26 01:44:07
【问题描述】:

无论x.y 的值如何,如何确定对象x 是否具有定义的属性y

我正在使用

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

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

【问题讨论】:

标签: javascript object


【解决方案1】:

除了其他答案,我想建议使用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 运算符进行验证,正如其他答案所建议的那样。

【讨论】:

    【解决方案2】:

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

    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 允许断言属性是只读数组的成员。您可以在此blog post 中阅读有关包含的更多信息。

    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 推断的值不同。

    【讨论】:

    • 感谢您抽出宝贵时间回答!最好能解释一下你的答案,而不仅仅是代码。
    【解决方案3】:

    包括

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

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

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

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

    【讨论】:

      【解决方案4】:
      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 }
      

      【讨论】:

        【解决方案5】:

        ES6+:

        ES6+ 上有一个新特性,你可以像下面这样检查它:

        if (x?.y)
        

        实际上,解释器会检查x 的存在,然后调用y,因为在if 括号内会发生强制转换并将x?.y 转换为布尔值。

        【讨论】:

        • 如果 x.y 存在并且它是一个 falsy 值怎么办。
        【解决方案6】:

        Underscore.js 或 Lodash

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

        :)

        【讨论】:

        • 不。它只是Object.prototype.hasOwnProperty.call(x, "y") 的别名。对于数组,我认为您可能需要 Array.prototype.indexOf_.indexOf_.contains
        【解决方案7】:

        为什么不简单:

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

        或者,如果您期望特定类型:

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

        【讨论】:

        • 因为它不好阅读并且不严格输入。我必须问你:为什么不简单地x.hasOwnProperty('y')
        【解决方案8】:

        由于问题是关于属性检查的笨拙,以及一个用于验证函数参数选项对象的常规用例,我想我会提到一种测试多个属性是否存在的无库的简短方法。 免责声明:它确实需要 ECMAScript 5(但 IMO 任何仍在使用 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
        

        【讨论】:

          【解决方案9】:

          对象有属性:

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

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

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

          您也可以使用in 运算符来测试继承的属性。

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

          【讨论】:

          • 或者更好——Object.prototype.hasOwnProperty.call(x, 'y'),这样名为“hasOwnProperty”的属性就不会与检查过程发生冲突;)
          • 甚至更短——{}.hasOwnProperty.call(x, 'y').
          【解决方案10】:

          我的原始代码的一个功能

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

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

          所以也许这三种方法都在一个人的花招中占有一席之地。

          【讨论】:

          • 你总是可以使用(x &amp;&amp; x.hasOwnProperty('y'))(x &amp;&amp; 'y' in x)
          • 我同意,对 x 的测试应该是一个单独的案例。还可以生成更好的错误报告。
          • 这对我来说失败了。如果 x 未定义,则 typeof(x.y) 返回 ReferenceError 而不是字符串 'undefined'
          【解决方案11】:

          如果您想知道对象是否在物理上 包含属性@gnarf's,则使用hasOwnProperty 回答即可。

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

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

          例如:

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

          【讨论】:

            【解决方案12】:

            你可以像这样修剪一下:

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

            【讨论】:

            • x = {y:undefined} 会失败
            • 有人需要区分“未定义”和“定义为未定义”吗?
            • @darkporter 我有时会这样做 ;)
            • @jpsimons for and instance "defined to be undefined" 由 Firestore 在保存文档数据时使用。
            猜你喜欢
            • 2013-10-18
            • 2012-03-06
            • 2018-05-06
            • 2019-02-11
            • 1970-01-01
            • 2012-06-09
            • 2015-03-01
            • 1970-01-01
            相关资源
            最近更新 更多