Skip to content

hasOwnProperty 的边界情况:它真的能安全判断自有属性吗?什么情况下会失效?

hasOwnProperty 不是语言的语法关键字,而是一个可被覆盖的普通方法——它的安全性依赖于 Object.prototype 的完整性。”

当你写下:

js
obj.hasOwnProperty('x')

你可能认为这是一条“绝对安全”的自有属性检测指令。

但真相是:
hasOwnProperty 本身只是一个定义在 Object.prototype 上的方法,
它可以被对象自身覆盖、删除,甚至整个原型链可以被污染。

我们来从 原型机制、覆盖场景、安全调用方式、替代方案 四个维度,彻底拆解:

一、hasOwnProperty 的本质:一个可枚举的原型方法

来自 ECMA-262 §20.1.3.4

Object.prototype.hasOwnProperty(V) 方法返回一个布尔值,表示对象是否具有给定名称的自有属性。

其内部执行逻辑是:

text
function hasOwnProperty(V) {
  const P = ToPropertyKey(V);
  const O = ToObject(this);
  return ? O.[[GetOwnProperty]](P) !== undefined;
}

它的本质是:调用目标对象的 [[GetOwnProperty]] 内部方法,不查找原型链。

二、边界情况 1:hasOwnProperty 被对象自身覆盖

js
const obj = {
  hasOwnProperty: 'hijacked'
};

obj.hasOwnProperty('x'); // 报错:obj.hasOwnProperty is not a function

因为 obj 自身定义了 hasOwnProperty 属性,
其值为字符串 'hijacked',不是函数,
所以调用时会抛出 TypeError

三、边界情况 2:hasOwnProperty 被显式设置为非函数

js
const obj = {};
obj.hasOwnProperty = null;

obj.hasOwnProperty('x'); // TypeError: is not a function

即使你没有在字面量中定义,
后续代码仍可能意外覆盖它。

四、边界情况 3:Object.prototype 被污染(全局性风险)

js
Object.prototype.hasOwnProperty = 'corrupted';

const obj = { x: 1 };
obj.hasOwnProperty('x'); // TypeError: is not a function

虽然现代开发中极少直接修改 Object.prototype
但在沙箱环境、第三方库冲突或恶意代码中,这种污染是可能的。

五、边界情况 4:null 原型对象无法访问 hasOwnProperty

js
const obj = Object.create(null);
obj.x = 1;

obj.hasOwnProperty('x'); // TypeError: obj.hasOwnProperty is not a function

因为 obj.[[Prototype]] = null
它不继承 Object.prototype
所以根本没有 hasOwnProperty 方法。

六、安全调用方式:使用 call 绕过覆盖

最经典的解决方案是:

js
Object.prototype.hasOwnProperty.call(obj, 'x');

为什么这样安全?

  • Object.prototype.hasOwnProperty 是原始函数,不会被实例覆盖
  • 使用 call 显式指定 thisobj
  • 即使 obj 自身有 hasOwnProperty 属性,也不会影响调用
js
const obj = { hasOwnProperty: 'evil' };
Object.prototype.hasOwnProperty.call(obj, 'x'); // 正常工作

七、更现代的替代方案:Object.hasOwn

来自 ECMA-262 §20.1.2.3

Object.hasOwn(obj, prop)hasOwnProperty 的静态版本,推荐替代方案。

js
Object.hasOwn(obj, 'x');

优势:

  • 静态方法,不依赖 this 或原型链
  • 不会被覆盖(Object.hasOwnObject 上的属性)
  • 语义更清晰
  • 专门处理 nullundefined(返回 false
js
Object.hasOwn({ hasOwnProperty: 'x' }, 'x'); // true
Object.hasOwn(Object.create(null), 'x');      // 取决于是否存在
Object.hasOwn(null, 'x');                     // false(安全)

兼容性:现代浏览器和 Node.js 支持,旧环境可使用 polyfill。

八、与 in 操作符的对比:何时使用哪个?

场景推荐方法原因
检查自有属性(不包括继承)Object.hasOwn(obj, 'prop')安全、语义明确
检查属性是否存在(包括继承)'prop' in obj原型链查找
旧环境且需兼容Object.prototype.hasOwnProperty.call(obj, 'prop')最广泛支持的安全方式
知道 obj 一定继承 Object.prototype 且未被覆盖obj.hasOwnProperty('prop')简洁,但风险高

九、hasOwnProperty 的返回逻辑:基于 [[GetOwnProperty]]

无论调用方式如何,hasOwnProperty 的核心逻辑始终是:

js
[[GetOwnProperty]](P) !== undefined

这意味着:

  • 只要属性是自有属性(own property),无论 enumerableconfigurable 如何,都返回 true
  • 包括不可枚举属性、Symbol 属性
js
const obj = {};
Object.defineProperty(obj, 'hidden', {
  value: 1,
  enumerable: false
});

Object.hasOwn(obj, 'hidden'); // true

一句话总结

obj.hasOwnProperty('x') 并不总是安全的,它在以下情况会失效:

  1. obj 自身覆盖了 hasOwnProperty 属性
  2. obj 的原型链被污染,导致 hasOwnProperty 不是函数
  3. obj 的原型为 null(如 Object.create(null)

安全做法是:

  • 使用 Object.prototype.hasOwnProperty.call(obj, 'x')
  • 或使用现代的 Object.hasOwn(obj, 'x')

结语:理解 hasOwnProperty 的边界,就是理解“原型系统的开放性”

大多数人认为 hasOwnProperty 是“内置语法”,
而你理解的是:

“它只是一个普通的原型方法,其安全性依赖于 Object.prototype 的完整性;
真正的自有属性检测,必须绕过实例的潜在污染,直接调用原始方法或使用静态替代。”

你不再只是“调用方法”,
而是在防御语言的动态性带来的风险