`Object.hasOwn` 检查对象是否包含某一属性参数

本文首发于我的个人 Blog,永久地址: https://tie.pub/2021/11/objec...,
欢迎大家订阅。

在过去我们判断某个字符串变量是否是对象的自有属性时,通常使用 Object 对象原型链上的 hasOwnProperty 方法来判断:

const hasOwnProperty = Object.prototype.hasOwnProperty;

if (hasOwnProperty.call(object, 'foo')) {
  // `object` 包含属性 `foo`.
}

既然 Object.prototype 包含 hasOwnProperty 方法,且可以判断属性参数,为什么继承 Object 属性的 object 不直接使用 object.hasOwnProperty 呢?答案是 JavaScript 普通对象的原型可以被覆盖,使用 Object.prototype.hasOwnProperty 可以确保安全正确。

const baz = {
  hasOwnProperty: function () {
    return false;
  },
  ba: 'own property',
};

可以看到 baz 对象拥有重新定义的 hasOwnProperty 方法,该方法覆盖从 Object.prototype 继承的方法 hasOwnProperty,直接调用 baz.hasOwnProperty('ba') 只会返回 false。

另外 Object.create(null) 会创建一个空对象,该空对象没有继承 Object.prototype 对象,所以会报如下错误:

Object.create(null).hasOwnProperty('foo');
// Uncaught TypeError: Object.create(...).hasOwnProperty is not a function

Object.hasOwn 提案

现在 ECMAScript 官方正式提出 Object.hasOwn 提案,快捷判断对象自有属性,提高代码可读性。

if (Object.hasOwn(object, 'foo')) {
  // `object` 包含属性 `foo`.
}

提案方法 Object.hasOwnObject.prototype.hasOwnProperty.call(object, property) 具有相同的行为:

let object = { foo: false };
Object.hasOwn(object, 'foo'); // true

let object2 = Object.create({ foo: true });
Object.hasOwn(object2, 'foo'); // false

let object3 = Object.create(null);
Object.hasOwn(object3, 'foo'); // false

let object4 = { foo: undefined, baz: null };
Object.hasOwn(object4, 'foo'); // true
Object.hasOwn(object4, 'baz'); // true

Object.hasOwn(example, 'toString'); // false
Object.hasOwn(example, 'hasOwnProperty'); // false

in 操作符

有时候我们根据需要使用 in 操作符判断某参数是否是对象的属性:

let object = { foo: false };
if ('foo' in object) {
  // `foo` 是 `object` 的属性
}

in 操作符与 Object.hasOwn 的不同在于除了判断对象的自有属性外,还会检查原型链上是否包含这一属性:

let object = {};
'toString' in object; // true
'hasOwnProperty' in object; // true
Object.hasOwn('toString'); //false

for ... in 循环

JavaScript 中可以使用 for in 循环遍历一个对象,由于 in 操作符会检查原型链。所以需要使用 Object.hasOwn 跳过继承属性:

let object = { foo: true, bar: true };
for (let name in object) {
  if (Object.hasOwn(object, name)) {
    // ...
  }
}

检查数组是否包含某一个索引

由于数组 Array 是一种特殊的对象,我们也可以使用 Object.hasOwn 判断数组的索引:

let friends = ['吴文俊', '李星', '小郭', '浩哥', '小白龙'];
Object.hasOwn(friends, 2); // true - '小郭'
Object.hasOwn(friends, 5); // false

polyfill 支持

在不支持的浏览器中,我们需要使用一些回退方式。我们利用 Object.hasOwnObject.prototype.hasOwnProperty.call(object, property) 的相同行为实现支持方案:

if (!Object.hasOwn) {
  Object.defineProperty(Object, 'hasOwn', {
    value: function (object, property) {
      if (object == null) {
        throw new TypeError('Cannot convert undefined or null to object');
      }
      return Object.prototype.hasOwnProperty.call(Object(object), property);
    },
    configurable: true,
    enumerable: false,
    writable: true,
  });
}

除此之外我们在平时的开发过程中也会使用社区提供的工具库 haslodash.has,它们都能很好地判断对象的属性。

浏览器支持情况

截至文章发布日 2021 年 11 月 9 日,浏览器对 Object.hasOwn 的支持情况如下:

  • Chrome 93+
  • Firefox 92+
  • Safari 不支持
  • Edge 93+

推荐阅读

  1. ES 提案:可选链(optional chaining)
  2. String.prototype.replaceAll

你可能感兴趣的