object.hasown 性能最优,直接属性访问次之,in 操作符较慢,手写遍历最差;需控制原型链深度为唯一变量,用 benchmark.js 量化并结合火焰图验证 ic 命中情况。

直接用 Benchmark.js 封装两种逻辑,控制原型链结构、键名、访问方式三个变量,再结合 DevTools 火焰图验证 IC 命中情况,就能定量区分手写遍历(如循环 __proto__)和标准操作符(in、Object.hasOwn、obj.x)的真实开销。
构造可控的原型链测试对象
必须让“查找深度”成为唯一变量,其他因素全部固定:
- 用
Object.setPrototypeOf逐层构建,例如:let obj = {}; for (let i = 0; i - 确保目标属性(如
'id')只定义在最底层原型上,这样每次查找都必须走满整条链 - 避免动态添加属性或篡改
__proto__,否则会触发 V8 的 IC miss,污染测试结果 - 超过 16 层时 V8 会降级为慢路径,建议测试点设为 0、4、8、12、16 层,覆盖关键拐点
封装纯函数做对比测试
每种操作单独封装,不混入初始化、日志、随机数等副作用:
- 手写遍历(模拟
in行为):function hasInChain(obj, key) { while (obj != null) { if (Object.prototype.hasOwnProperty.call(obj, key)) return true; obj = Object.getPrototypeOf(obj); } return false; } - 标准
in操作符:function inOp(obj, key) { return key in obj; } - 标准
Object.hasOwn:function hasOwn(obj, key) { return Object.hasOwn(obj, key); } - 直接属性访问:
function getProp(obj, key) { return obj[key]; }
所有函数接收相同预构造的 obj 和字面量 key(如 'id'),不使用变量键名。
运行基准并解读关键指标
用 Benchmark.js 运行后重点关注三项输出:
-
Hz 值:越高说明单位时间执行次数越多,即越快。通常
Object.hasOwn> 直接访问 >in> 手写遍历 - ±% 误差:若大于 1.5%,说明环境干扰大,需重测(如关闭浏览器其他标签页、禁用插件)
-
火焰图中的 Scripting 占比与 IC 状态:在 Chrome Performance 面板中录制高频调用(如循环 10 万次),观察是否出现大量
GetPropertyStub或LoadIC_Miss—— 出现即表明引擎未能优化该访问路径
区分语义再选方法
性能不是唯一标准,不同操作解决的问题本质不同:
-
obj.x是读取值,引擎可走最快 IC 路径;'x' in obj是判断存在性,必须遍历整条链;Object.hasOwn(obj, 'x')只查自身,不走原型链,现代引擎已内联优化 - 别先
in再obj.x,这是双重开销;需要值就直接访问,需要存在性就用hasOwn - 手写遍历仅在极特殊场景有用(如需跳过某些原型、或兼容不支持
Object.hasOwn的旧环境),日常开发中无性能优势











