要判断javascript对象的属性是否为自身属性而非继承自原型链,应使用hasownproperty方法。1. 使用对象的hasownproperty()方法可直接判断属性是否为自身所有,返回true表示是自身属性,false表示来自原型链或不存在;2. 为避免对象自身hasownproperty被覆盖导致异常,应使用object.prototype.hasownproperty.call(obj, ‘prop’)以确保调用原生方法;3. in操作符可用于检查属性是否存在于对象或其原型链上,只要存在即返回true;4. object.keys()返回对象自身所有可枚举的字符串键属性;5. object.getownpropertynames()返回自身所有字符串键属性,包括不可枚举的;6. object.getownpropertysymbols()返回自身所有symbol类型属性;7. object.getownpropertydescriptor()获取自身属性的完整描述符,若属性不存在则返回undefined;8. 原型链上的属性遮蔽不会影响hasownproperty的判断,它始终只关注对象自身是否定义了该属性;9. 修改原型会影响所有继承该原型的对象的属性访问,但不会改变hasownproperty对这些对象的判断结果,因其仍仅检测自身属性。因此,hasownproperty是区分自身与继承属性的可靠标准,结合其他方法可根据不同场景精准控制属性操作,避免遍历或配置时引入原型链上的意外属性,确保代码行为准确无误。

要弄清楚一个JavaScript对象的某个属性究竟是它自己的“私有财产”,还是从它爹妈(原型链)那里继承来的,其实有个非常直接且稳妥的办法。核心就是用 hasOwnProperty 方法。

解决方案
在我看来,hasOwnProperty就是那个金标准。它会检查对象本身是否拥有某个属性,而不会去原型链上找。这意味着,如果一个属性是从原型链上继承来的,hasOwnProperty就会返回 false。只有当属性是对象直接定义的,它才会返回 true。
举个例子:

function Person(name) { this.name = name;}Person.prototype.greet = function() { console.log(`Hello, I'm ${this.name}`);};const john = new Person('John');john.age = 30; // 自身属性console.log(john.hasOwnProperty('name')); // true,因为name是john自身定义的console.log(john.hasOwnProperty('age')); // true,age也是john自身定义的console.log(john.hasOwnProperty('greet')); // false,greet是原型链上的方法console.log(john.hasOwnProperty('toString')); // false,toString也是原型链上的方法console.log(john.hasOwnProperty('occupation')); // false,属性不存在// 有时候,对象可能会覆盖hasOwnProperty方法,这虽然不常见但确实可能发生。// 为了绝对安全,尤其是在处理来自外部的、不可控的对象时,// 最好使用 Object.prototype.hasOwnProperty.call()。// 这样可以确保始终调用的是原生的 hasOwnProperty 方法。const objWithBadHasOwnProperty = { a: 1, hasOwnProperty: 'oops, I broke it'};// console.log(objWithBadHasOwnProperty.hasOwnProperty('a')); // 这会报错或行为异常console.log(Object.prototype.hasOwnProperty.call(objWithBadHasOwnProperty, 'a')); // true,安全可靠console.log(Object.prototype.hasOwnProperty.call(john, 'greet')); // false,同样可靠
为什么区分自身属性和原型属性很重要?
这玩意儿为什么重要呢?我个人在工作中遇到过不少次,不分清楚这俩就容易踩坑。最典型的场景就是遍历对象属性的时候。我们经常会用 for...in 循环来遍历一个对象的属性,但 for...in 不仅仅会遍历对象自身的枚举属性,它还会遍历原型链上的可枚举属性。这在很多情况下都不是我们想要的。比如你只想处理对象自己的数据,却意外地操作了原型上的方法,那可就麻烦了。
再举个例子,假设你有一个配置对象,想把它的所有自身属性保存起来。如果你直接用 for...in,然后不加 hasOwnProperty 过滤,你可能就把 toString、valueOf 这些方法也当成配置项给存进去了,这显然是错的。

const myConfig = { theme: 'dark', fontSize: 16};// 假设原型链上不小心加了个东西(虽然不推荐这样做)Object.prototype.debugMode = true;const allProps = [];for (const key in myConfig) { // 如果没有这个判断,debugMode也会被加进来 if (myConfig.hasOwnProperty(key)) { allProps.push(key); }}console.log(allProps); // ['theme', 'fontSize'],这才是我们想要的// 如果没有hasOwnProperty,会变成:const allPropsWithoutCheck = [];for (const key in myConfig) { allPropsWithoutCheck.push(key);}// console.log(allPropsWithoutCheck); // ['theme', 'fontSize', 'debugMode'],这就不对了
所以,区分自身和原型属性,能让你更精准地控制代码行为,避免不必要的副作用。
除了hasOwnProperty,还有哪些方法可以检查属性来源或相关信息?
那除了hasOwnProperty,还有别的招儿吗?当然有,不过它们侧重点不一样,用起来得看具体需求。
一个常见的操作符是 in。in 操作符会检查属性是否在对象或其原型链上,它不像 hasOwnProperty 那么严格,只要能找到就返回 true。
const car = { brand: 'Tesla'};Object.setPrototypeOf(car, { engine: 'electric'}); // 设置原型console.log('brand' in car); // trueconsole.log('engine' in car); // trueconsole.log(car.hasOwnProperty('brand')); // trueconsole.log(car.hasOwnProperty('engine')); // false
如果你想获取对象自身的所有属性名(不包括原型链上的),可以用 Object.keys()。它只会返回对象自身可枚举的字符串键。
const obj = { a: 1, b: 2 };Object.defineProperty(obj, 'c', { value: 3, enumerable: false // 不可枚举});Object.setPrototypeOf(obj, { d: 4 });console.log(Object.keys(obj)); // ['a', 'b'],不包含c(不可枚举)和d(原型上的)
如果你需要获取对象自身所有属性名,包括不可枚举的,那就要用到 Object.getOwnPropertyNames()。如果还有Symbol类型的属性,就得用 Object.getOwnPropertySymbols()。
const myObj = { prop1: 'value1', [Symbol('id')]: 123};Object.defineProperty(myObj, 'hiddenProp', { value: 'secret', enumerable: false});console.log(Object.getOwnPropertyNames(myObj)); // ['prop1', 'hiddenProp']console.log(Object.getOwnPropertySymbols(myObj)); // [Symbol(id)]
Object.getOwnPropertyDescriptor() 则能返回一个属性的完整描述符,包括它的值、是否可写、可枚举、可配置等信息。这个方法只针对自身属性。
const descObj = { x: 10 };const descriptor = Object.getOwnPropertyDescriptor(descObj, 'x');console.log(descriptor);/*{ value: 10, writable: true, enumerable: true, configurable: true}*/const nonExistentDesc = Object.getOwnPropertyDescriptor(descObj, 'y');console.log(nonExistentDesc); // undefined
这些方法各有侧重,hasOwnProperty 是判断“是否是自己的”,in 是判断“有没有这个”,Object.keys 等是“列出自己的”。根据你的具体场景,选择最合适的工具就好。
原型链操作对属性检查有何影响?
原型链这东西,JavaScript的精髓之一,但它也确实能让属性查找变得有点儿意思。当你在原型链上添加或修改属性时,这会直接影响到属性的查找行为,但对 hasOwnProperty 的判断逻辑,影响其实很小,因为它只关心对象自身。
比如,你有一个对象,它的原型上有一个 methodA。如果你在对象自身也定义了一个 methodA,那么对象自身的 methodA 就会“遮蔽”掉原型上的那个。这就是所谓的“属性遮蔽”(shadowing)。
const proto = { value: 'from prototype', sayHello() { console.log('Hello from prototype'); }};const obj = Object.create(proto);obj.ownValue = 'from instance';console.log(obj.value); // 'from prototype'obj.sayHello(); // 'Hello from prototype'// 遮蔽原型属性obj.value = 'from instance, shadowing prototype';obj.sayHello = function() { console.log('Hello from instance'); };console.log(obj.value); // 'from instance, shadowing prototype'obj.sayHello(); // 'Hello from instance'console.log(obj.hasOwnProperty('value')); // true (因为obj自身现在有了value)console.log(obj.hasOwnProperty('sayHello')); // true (因为obj自身现在有了sayHello)console.log(obj.hasOwnProperty('ownValue')); // true
你会发现,即使原型上的属性被遮蔽了,hasOwnProperty 依然只关心 obj 自身有没有这个属性。它不会去管原型链上有没有一个同名的、被遮蔽的属性。这正是 hasOwnProperty 的强大之处,它提供了一个清晰的边界。
但是,如果你直接修改了原型上的属性,那所有继承自这个原型的实例都会受到影响。
const Animal = { type: 'mammal'};const dog = Object.create(Animal);const cat = Object.create(Animal);console.log(dog.type); // 'mammal'console.log(cat.type); // 'mammal'// 修改原型上的属性Animal.type = 'vertebrate';console.log(dog.type); // 'vertebrate'console.log(cat.type); // 'vertebrate'console.log(dog.hasOwnProperty('type')); // falseconsole.log(cat.hasOwnProperty('type')); // false
在这种情况下,hasOwnProperty 依然返回 false,因为它知道 type 属性不是 dog 或 cat 自身的。这再次强调了 hasOwnProperty 的设计目标:只关注对象自身。所以,无论原型链怎么变,只要不是在对象自身上直接添加或修改属性,hasOwnProperty 的判断结果就不会改变。这使得它在处理复杂继承关系时,依然是一个非常可靠的工具。
以上就是js怎么判断属性来自原型还是自身的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1512803.html
微信扫一扫
支付宝扫一扫