☰
core-js 中的 `Object.hasOwn`:从“可访问的 hasOwnProperty“提案到 ES2022 标准实现
2026/9/28 1:42:26 网站建设 项目流程

core-js 中的Object.hasOwn:从"可访问的 hasOwnProperty"提案到 ES2022 标准实现

【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js

本文以 core-js 文档中 AccessibleObject.prototype.hasOwnProperty页面为核心线索,系统讲解Object.hasOwn静态方法的提案动机、标准语义、在 core-js 中的模块化实现与分层入口,并结合仓库源码与单元测试给出可直接运行的接入示例。读完本文,你将掌握Object.hasOwn与hasOwnProperty、in的本质差异,知道在 core-js 中按需引入该特性的准确入口,并能从源码层面理解其 polyfill 的兜底逻辑与边界行为。

提案背景:为什么需要一个"可访问"的hasOwnProperty

长久以来,判断一个对象是否"自身拥有"某个属性,最可靠的写法是借用原型上的方法:

Object.prototype.hasOwnProperty.call(obj, 'key');

这个写法虽然正确,但存在三个实际痛点:

  1. 冗长且易错:每次都要写Object.prototype.hasOwnProperty.call,拼写长、容易把prototype写错或漏掉.call;
  2. 原型链污染风险:如果直接调用obj.hasOwnProperty(key),一旦obj上恰好定义了hasOwnProperty属性(或被第三方库覆盖),就会调用到错误实现甚至抛出TypeError;
  3. 对null/undefined不安全:Object.prototype.hasOwnProperty.call(null, key)在严格模式下会抛错,行为不统一。

为此,TC39 提出了 AccessibleObject.prototype.hasOwnProperty提案,核心思路是提供一个挂载在Object构造函数上的静态方法Object.hasOwn,从而彻底绕开原型调用路径。该提案已合入 ECMAScript 标准,Object.hasOwn成为 ES2022(对应 ECMA-262 第 13 版)的正式内置方法。

标准签名与语义

根据关联文档中给出的签名,Object.hasOwn是Object上的一个静态方法:

class Object { static hasOwn(object: object, key: PropertyKey): boolean; }

其语义要点:

  • Object.hasOwn(object, key)返回布尔值,表示key是否为object的自身属性(own property),不沿原型链查找;
  • 与实例方法不同,它不要求object是对象:当object为null或undefined时,直接抛出TypeError,而不会先尝试把参数隐式转换为对象再继续(这是所谓的"现代行为",下文测试部分会验证);
  • key可以是字符串或 Symbol 等PropertyKey,内部与HasOwnProperty抽象操作一致。

与相关 API 的对比

API查找范围对 null/undefined 的行为是否需要借助.call
Object.hasOwn(obj, key)仅自身属性抛TypeError(不做隐式转换)否
Object.prototype.hasOwnProperty.call(obj, key)仅自身属性在严格模式下抛错 / 行为不统一是
obj.hasOwnProperty(key)仅自身属性不安全:可被实例属性遮蔽否,但有污染风险
key in obj自身属性 + 原型链不抛错,但语义是"可访问"而非"拥有"否

core-js 中的模块实现

入口模块es.object.has-own

Object.hasOwn属于 ES 稳定特性,core-js 将其实现为独立模块 packages/core-js/modules/es.object.has-own.js:

'use strict'; var $ = require('../internals/export'); var hasOwn = require('../internals/has-own-property'); // `Object.hasOwn` method // https://tc39.es/ecma262/#sec-object.hasown $({ target: 'Object', stat: true }, { hasOwn: hasOwn });

它通过 core-js 内部的export工具,以target: 'Object', stat: true的方式把hasOwn挂载为Object的静态方法。

底层兜底实现internals/has-own-property.js

真正的实现位于 packages/core-js/internals/has-own-property.js:

'use strict'; var uncurryThis = require('../internals/function-uncurry-this'); var toObject = require('../internals/to-object'); var hasOwnProperty = uncurryThis({}.hasOwnProperty); // `HasOwnProperty` abstract operation // https://tc39.es/ecma262/#sec-hasownproperty module.exports = Object.hasOwn || function hasOwn(it, key) { return hasOwnProperty(toObject(it), key); };

这里体现了 core-js 的标准做法——原生优先,缺失才兜底:

  1. uncurryThis({}.hasOwnProperty)先把原生hasOwnProperty提取为可直接调用的函数(核心实现采用同样的uncurryThis技巧,参见 packages/core-js/internals/function-uncurry-this.js);
  2. 若运行环境已原生支持Object.hasOwn,则直接返回原生方法,不重复注入;
  3. 否则退回 polyfill:先经toObject(it)把非对象参数做 ToObject 转换(对null/undefined抛TypeError),再调用解绑后的hasOwnProperty完成判断。

值得注意:internals/has-own-property.js中该内部工具本身也被其他模块复用(如集合类、元数据相关实现),说明Object.hasOwn底层逻辑同时承担着 core-js 内部大量"判断自身属性"的公共职责。

分层入口与引用方式

按命名空间按需引入

关联文档指明该特性的 entry point 为:

core-js/proposals/accessible-object-hasownproperty

对应文件 packages/core-js/proposals/accessible-object-hasownproperty.js,其内部仅一行:

require('../modules/esnext.object.has-own');

由于该特性已标准化,core-js 提供了从"纯 ES"到"全量"的完整入口体系(详见 docs/web/docs/usage.md),你可以按需选择:

// 仅稳定 ES 特性(推荐按需引入) import "core-js/es/object/has-own"; // 稳定 + Web 标准 + stage 3 提案 import "core-js/actual/object/has-own"; // 包含早期提案的全量入口 import "core-js/full/object/has-own"; // 一次性引入某命名空间下的全部特性 import "core-js/es/object"; // 使用 core-js-pure,避免污染全局命名空间 import "core-js-pure/es/object/has-own";

其中:

  • packages/core-js/es/object/has-own.js 等 "es" 层入口直接 require 对应模块;
  • es/object/index.js中已显式包含require('../../modules/es.object.has-own')(见 packages/core-js/es/object/index.js);
  • packages/core-js/full/object/has-own.js 会在实际入口之外再require('../../modules/esnext.object.has-own'),并带有TODO: Remove from core-js@4注释——因为它在 core-js 4 中将被移除。

esnext.object.has-own:提案期的兼容别名

packages/core-js/modules/esnext.object.has-own.js 是提案期的遗留入口,现在仅作为兼容别名存在,内容同样是转发到标准模块:

'use strict'; // TODO: Remove from `core-js@4` require('../modules/es.object.has-own');

也就是说:当前仓库版本下,无论走proposals/accessible-object-hasownproperty还是esnext.object.has-own,最终注入的始终是标准的es.object.has-own模块。从源码结构可以推断,这是 core-js 在特性从 stage 3 提案晋级为标准后保留的一层平滑过渡,帮助早期按提案名引入的用户无痛升级。

单元测试:行为契约的完整验证

core-js 为Object.hasOwn提供了专门的 QUnit 测试 tests/unit-global/es.object.has-own.js,可作为该 API 的"行为契约"文档:

QUnit.test('Object.hasOwn', assert => { const { create, hasOwn } = Object; assert.isFunction(hasOwn); assert.arity(hasOwn, 2); assert.name(hasOwn, 'hasOwn'); assert.looksNative(hasOwn); assert.nonEnumerable(Object, 'hasOwn'); assert.true(hasOwn({ q: 42 }, 'q')); assert.false(hasOwn({ q: 42 }, 'w')); assert.false(hasOwn(create({ q: 42 }), 'q')); assert.true(hasOwn(Object.prototype, 'hasOwnProperty')); let called = false; try { hasOwn(null, { toString() { called = true; } }); } catch { /* empty */ } assert.false(called, 'modern behaviour'); assert.throws(() => hasOwn(null, 'foo'), TypeError, 'throws on null'); assert.throws(() => hasOwn(undefined, 'foo'), TypeError, 'throws on undefined'); });

测试断言覆盖了核心契约:

  • 形态:是函数、参数长度为 2、名为hasOwn、looksNative(不被误判为第三方注入)、在Object上不可枚举;
  • 语义:自身属性返回true;不存在的键返回false;Object.create({ q: 42 })的原型链属性返回false(不沿原型链查找);Object.prototype自身拥有hasOwnProperty因此返回true;
  • 边界(modern behaviour):传入null时不会先对第二个参数做属性键转换(自定义toString未被调用),而是直接抛TypeError——这正是Object.hasOwn与旧式借用写法最大的行为差异,也印证了 polyfill 中"先toObject再判断"的顺序。

实战建议

  1. 新代码统一使用Object.hasOwn,替代Object.prototype.hasOwnProperty.call(...)与obj.hasOwnProperty(key)两种旧写法,彻底规避原型污染问题;
  2. 按需引入:如果你的构建目标环境较老(如 IE11 或旧版 Node),引入core-js/es/object/has-own(或core-js/stable中的对应入口)即可获得与原生一致的实现,polyfill 会优先使用引擎原生方法,性能与行为均无损失;
  3. 关注命名空间差异:/es/仅覆盖稳定标准特性;若项目同时依赖早期提案,推荐用文档建议的/actual/命名空间(包含全部正式特性与 stage 3 提案);full与esnext别名入口保留提案期命名,在 core-js 4 中会被移除,不应作为长期依赖;
  4. 测试即文档:接入后可直接参考 tests/unit-global/es.object.has-own.js 中的断言,用相同用例校验你的运行环境行为,尤其是对null/undefined抛TypeError且不触发隐式转换的"现代行为"。

【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询