
当类中同时存在同名的公有字段(如 `method = "sss"`)和子类方法(如 `method() {}`)时,实例优先访问的是**自身拥有的字段属性**,而非原型链上的同名方法,这是由属性设置时机、位置(own property vs. prototype)及 javascript 原型机制共同决定的。
在 JavaScript 类中,method = "sss" 和 method() {} 表面相似,实则语义与运行机制截然不同:
✅ 公有字段(Public Class Fields):实例自有属性
class Parent {
method = "sss"; // 等价于在 constructor 中执行 this.method = "sss"
}- 在每次 new Parent() 或 new Child() 时,构造函数执行阶段将 "sss" 直接赋值给实例对象自身(即 this.method);
- 该属性是 own property(自有属性),可通过 obj.hasOwnProperty('method') === true 验证;
- 它覆盖(shadow)了原型链上同名的方法——即使子类或父类原型定义了 method(),只要实例自身有 method 字段,调用 child.method 就会返回字符串 "sss",而非执行函数。
✅ 方法定义:挂载在原型上
class Parent {
method() { console.log("Parent"); }
}
// 等价于:
Parent.prototype.method = function() { console.log("Parent"); };- 方法不绑定到实例,而是添加到 Parent.prototype 上;
- 所有实例共享该方法,通过原型链访问;
- child.hasOwnProperty('method') 返回 false,而 Parent.prototype.hasOwnProperty('method') 为 true。
? 实际行为验证示例
class Parent {
method = "sss";
parentMethod() { console.log("Parent method"); }
}
class Child extends Parent {
method() { console.log("Child method"); }
childMethod() { console.log("Child method"); }
}
const child = new Child();
console.log(child.method); // → "sss" (自有字段,优先命中)
console.log(child.hasOwnProperty("method")); // → true
console.log(typeof child.method); // → "string"
// 但方法依然存在于原型链中,可显式调用:
child.parentMethod(); // → "Parent method"
child.childMethod(); // → "Child method"
// 注意:child.method() 会报错!因为 "sss" 是字符串,不可调用
// TypeError: child.method is not a function⚠️ 关键注意事项
- 命名冲突无警告:JavaScript 不禁止字段与方法同名,但会导致方法被“隐藏”,极易引发静默逻辑错误;
- 继承时尤其危险:子类未重写字段,却重写了同名方法,父类字段仍会覆盖子类方法;
- 调试技巧:使用 Object.getOwnPropertyNames(child) 查看自有属性;用 Object.getPrototypeOf(child) 逐级检查原型方法;
- 最佳实践:避免在类中使用与方法同名的公有字段;若需默认值,改用 constructor 显式初始化,或采用私有字段(#method = "sss")+ 公共 getter/setter 明确封装意图。
简言之:字段赢在“实例优先”,方法赢在“原型共享”;同名时,自有属性永远胜出。理解这一机制,是写出可维护、可预期的 JavaScript 类的关键基础。










