hasOwnProperty 怎么区分自有属性与原型属性?Object.hasOwn 才是正解
判断一个属性是对象“自己拥有的”还是从原型链上继承来的,JavaScript 提供的正规方法是 hasOwnProperty()。但很多人写 obj.hasOwnProperty(key) 时会踩到两个坑:原型被污染导致方法不存在,以及 用 for...in 遍历时混入原型属性。本文说明两者差异与推荐写法。

一、原型链上的属性查找
访问 obj.x 时,JS 先在 obj 自身找,找不到就沿原型链逐级向上找,直到 Object.prototype。这带来一个副作用:“属性存在”不等于“属性是自己的”。
function User(name) { this.name = name; }
User.prototype.sayHi = function () { return 'hi'; };
const u = new User('Tom');
'name' in u; // true 自身属性
'sayHi' in u; // true 原型上的方法,in 也返回 true
u.hasOwnProperty('name'); // true 自有属性
u.hasOwnProperty('sayHi'); // false 在原型上,不是自己的
Object.keys(u); // ['name'] 只列自有可枚举属性
for (const k in u) console.log(k); // name 和 sayHi 都会被枚举出来
关键差异:in 看整条原型链,hasOwnProperty 只看自身,Object.keys 只看自身可枚举属性。
二、安全写法:不要用 obj.hasOwnProperty()
两种失败场景:
- 对象字面量用
Object.create(null)创建,没有原型,调用会抛TypeError; - 数据来自 JSON 解析,键名恰好包含
hasOwnProperty,该方法会被数据覆盖 —— 这是原型污染类漏洞的常见入口。
const bad = JSON.parse('{"hasOwnProperty": 1, "a": 2}');
bad.hasOwnProperty('a'); // TypeError: not a function
// 推荐写法1:借用原型方法
const has = Object.prototype.hasOwnProperty;
has.call(bad, 'a'); // true
// 推荐写法2:现代标准方法(不受数据污染影响)
Object.hasOwn(bad, 'a'); // true,ES2022
Object.hasOwn() 是目前最推荐的写法,但需要较新的运行环境(Node 16.9+/新版浏览器),旧环境用 has.call。
三、for...in 的正确用法
for (const key in obj) {
if (!Object.hasOwn(obj, key)) continue; // 过滤原型属性
console.log(key, obj[key]);
}
更进一步:遍历数据对象优先用 Object.keys/entries/values 而不是 for...in,它们天然只返回自有可枚举属性,且返回数组便于 map/filter。for...in 的设计目标是遍历对象属性(含原型),常用于检查类实例方法,而不是遍历数据。
四、可枚举、可写、可配置:三个描述符
“自有可枚举”是两层条件。用 getOwnPropertyDescriptor 可以看清楚:
const o = {};
Object.defineProperty(o, 'hidden', {
value: 1, enumerable: false, writable: true, configurable: true
});
Object.hasOwn(o, 'hidden'); // true 自有
Object.keys(o); // [] 不可枚举,不出现
JSON.stringify(o); // {} 序列化也看不到
所以库代码里“隐藏内部字段”常用不可枚举属性,这会让 Object.keys、JSON.stringify 都跳过它,但 hasOwnProperty 仍能检测到。
五、典型应用场景
- 合并配置时避免覆盖原型方法:深合并前先判断是否自有属性。
- Vue 2 的响应式初始化:遍历 data 时需要排除原型属性,正是用
hasOwnProperty判断(其源码中有hasOwnProperty.call的严格写法)。 - 防原型污染校验:写入
obj[key]前拒绝__proto__、constructor、prototype三个键,比事后检测更有效。
function safeSet(obj, key, val) {
if (['__proto__', 'constructor', 'prototype'].includes(key)) return;
obj[key] = val;
}
六、常见问题 FAQ
问:'x' in obj 和 obj.x !== undefined 哪个好?
答:都不如 Object.hasOwn。前者含原型;后者对显式赋值 undefined 的键会误判为不存在。
问:数组能用 hasOwnProperty 吗?
答:能,且是稀疏数组检测的正规手段:arr.hasOwnProperty(2) 可判断索引 2 是否真的存在。arr[2] === undefined 无法区分“空洞”与“存了 undefined”。
问:Object.getOwnPropertyNames 和 keys 区别?
答:前者包含不可枚举的自有属性(Symbol 除外),做对象自省时有用。
记忆要点:要“只判断自有属性”就用 Object.hasOwn(obj, k),旧环境写 Object.prototype.hasOwnProperty.call(obj, k);永远不要直接 obj.hasOwnProperty(k)。遍历数据用 Object.keys/entries,for...in 必须加自有属性过滤。
生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »
