DOMRect属性不可枚举的原因及原型可枚举get属性定义方法咨询
关于DOMRect属性不可枚举的原因及解决方案
一、为什么DOMRect的属性不可枚举?
这个设计其实是从性能优化、API规范合理性两个核心角度出发的:
- 减少频繁操作的性能开销:DOMRect是前端里被高频创建的对象——每次调用
getBoundingClientRect()都会生成新实例。如果这些属性设为可枚举,那么Object.keys()、Object.assign()或者for...in遍历这类操作都会额外处理这些属性,在大量操作DOMRect的场景下,这种不必要的开销会被明显放大。 - API的简洁性与安全性:DOM API的设计习惯把这类实例属性设为不可枚举,目的是引导开发者聚焦于属性的使用,而非意外地遍历或修改这些内部绑定的只读属性。同时,不可枚举的特性也能暗示这些是对象的“固有核心属性”,不应该被随意篡改。
- 原型属性的默认配置:这些属性本质是
DOMRectPrototype上的访问器属性(getter),根据ES规范,访问器属性的enumerable默认值就是false,所以自然无法被Object.keys()或Object.assign()识别到。
二、如何让DOMRect属性可枚举,方便对象比较?
有两种实用方案,你可以根据场景选择:
方案1:全局修改DOMRect原型,开启属性可枚举性
你可以通过Object.defineProperty()重新定义原型上的属性,复用原有getter逻辑的同时,把enumerable设为true:
// 列出DOMRect的所有标准属性 const rectPropertyNames = ['x', 'y', 'width', 'height', 'top', 'right', 'bottom', 'left']; rectPropertyNames.forEach(propName => { // 获取原型上原有属性的描述符 const originalDescriptor = Object.getOwnPropertyDescriptor(DOMRect.prototype, propName); if (originalDescriptor) { // 重新定义属性,开启enumerable Object.defineProperty(DOMRect.prototype, propName, { ...originalDescriptor, enumerable: true }); } });
修改后,Object.keys(document.body.getBoundingClientRect())就能返回所有属性名,Object.assign()也能正常复制这些属性,你的比较函数可以直接使用。
⚠️ 注意:这种方式是全局修改,会影响所有DOMRect实例,团队项目中使用要确保其他成员知情,避免引发意外问题。
方案2:不修改原型,用工具函数提取属性再比较
如果不想污染全局原型,更安全的方式是手动提取DOMRect的属性到普通对象,再进行比较:
// 提取DOMRect的所有属性到普通对象 function extractRectProps(rect) { return { x: rect.x, y: rect.y, width: rect.width, height: rect.height, top: rect.top, right: rect.right, bottom: rect.bottom, left: rect.left }; } // 比较两个DOMRect是否相等(考虑浮点数精度问题) function isRectEqual(rectA, rectB) { const propsA = extractRectProps(rectA); const propsB = extractRectProps(rectB); for (const key in propsA) { // 处理浮点数精度误差,比如允许小于0.000001的差值 if (Math.abs(propsA[key] - propsB[key]) > 1e-6) { return false; } } return true; }
这种方式不会影响全局环境,还能灵活处理DOMRect属性的浮点数精度问题(毕竟坐标、宽高通常带小数,直接用===比较可能有误差)。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

