core-js 中的 `Object.hasOwn`:从“可访问的 hasOwnProperty“提案到 ES2022 标准实现 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);这个写法虽然正确但存在三个实际痛点冗长且易错每次都要写Object.prototype.hasOwnProperty.call拼写长、容易把prototype写错或漏掉.call原型链污染风险如果直接调用obj.hasOwnProperty(key)一旦obj上恰好定义了hasOwnProperty属性或被第三方库覆盖就会调用到错误实现甚至抛出TypeError对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 的行为是否需要借助.callObject.hasOwn(obj, key)仅自身属性抛TypeError不做隐式转换否Object.prototype.hasOwnProperty.call(obj, key)仅自身属性在严格模式下抛错 / 行为不统一是obj.hasOwnProperty(key)仅自身属性不安全可被实例属性遮蔽否但有污染风险key in obj自身属性 原型链不抛错但语义是可访问而非拥有否core-js 中的模块实现入口模块es.object.has-ownObject.hasOwn属于 ES 稳定特性core-js 将其实现为独立模块 packages/core-js/modules/es.object.has-own.jsuse 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.jsuse 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 的标准做法——原生优先缺失才兜底uncurryThis({}.hasOwnProperty)先把原生hasOwnProperty提取为可直接调用的函数核心实现采用同样的uncurryThis技巧参见 packages/core-js/internals/function-uncurry-this.js若运行环境已原生支持Object.hasOwn则直接返回原生方法不重复注入否则退回 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.jspackages/core-js/full/object/has-own.js 会在实际入口之外再require(../../modules/esnext.object.has-own)并带有TODO: Remove from core-js4注释——因为它在 core-js 4 中将被移除。esnext.object.has-own提案期的兼容别名packages/core-js/modules/esnext.object.has-own.js 是提案期的遗留入口现在仅作为兼容别名存在内容同样是转发到标准模块use strict; // TODO: Remove from core-js4 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不存在的键返回falseObject.create({ q: 42 })的原型链属性返回false不沿原型链查找Object.prototype自身拥有hasOwnProperty因此返回true边界modern behaviour传入null时不会先对第二个参数做属性键转换自定义toString未被调用而是直接抛TypeError——这正是Object.hasOwn与旧式借用写法最大的行为差异也印证了 polyfill 中先toObject再判断的顺序。实战建议新代码统一使用Object.hasOwn替代Object.prototype.hasOwnProperty.call(...)与obj.hasOwnProperty(key)两种旧写法彻底规避原型污染问题按需引入如果你的构建目标环境较老如 IE11 或旧版 Node引入core-js/es/object/has-own或core-js/stable中的对应入口即可获得与原生一致的实现polyfill 会优先使用引擎原生方法性能与行为均无损失关注命名空间差异/es/仅覆盖稳定标准特性若项目同时依赖早期提案推荐用文档建议的/actual/命名空间包含全部正式特性与 stage 3 提案full与esnext别名入口保留提案期命名在 core-js 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),仅供参考