为何对象自有同名函数添加原型函数后无法访问?JS技术疑问
问题分析与解决
你遇到的这个错误其实不是因为原型方法覆盖了对象自身的方法,而是函数声明提升加上重复定义构造函数导致的问题,咱们一步步拆解:
1. 报错的核心原因
先看你的代码结构,你在同一个作用域里定义了两次person构造函数:
// 第一次定义:包含自身的greet方法 function person(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; this.greet = function() { console.log('hey there', this.lastname + ' ' + this.firstname); } } // ... 创建实例并调用greet // 第二次定义:没有自身的greet方法 function person(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; } person.prototype.greet = function() { console.log('hey there', this.lastname + ' ' + this.firstname); } // ... 创建新实例并调用greet
在JavaScript中,函数声明会被提升到当前作用域的最顶端,而且如果有同名的函数声明,后面的会直接覆盖前面的。也就是说,你的代码实际执行顺序是:
- 先执行第二个
person函数的声明(直接覆盖第一个) - 再执行第一段的实例创建代码——这时候调用的是没有
this.greet的构造函数,理论上这里就该报错,但你说前面的调用成功了,说明你大概率是分两段执行代码的(比如在控制台逐段输入)。
如果是分两段执行,那新实例调用greet报错的唯一可能是:你在创建新实例之后才给原型添加greet方法,或者赋值语句因为语法错误没执行到。不过回到你的核心疑问:原型上的同名方法会不会覆盖对象自身的方法?
答案是绝对不会!JavaScript的属性查找规则是:
- 先在对象自身查找属性/方法,找到就直接返回,不会再去原型链查找
- 只有自身找不到时,才会沿着原型链向上遍历
举个直观的例子:
function Person(name) { this.name = name; // 自身的greet方法 this.greet = function() { console.log('自身的greet:', this.name); } } // 原型上的同名方法 Person.prototype.greet = function() { console.log('原型的greet:', this.name); } const p = new Person('Alice'); p.greet(); // 输出:自身的greet: Alice
这里自身的greet完全屏蔽了原型上的版本,根本不会触发原型方法。
2. 修复你的代码
如果你想测试原型方法和自身方法的优先级,不需要重复定义构造函数,直接在原构造函数基础上给原型加方法即可:
// 只定义一次构造函数 function person(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; // 自身的greet方法 this.greet = function() { console.log('自身的greet:', this.lastname + ' ' + this.firstname); } } // 给原型添加同名方法 person.prototype.greet = function() { console.log('原型的greet:', this.lastname + ' ' + this.firstname); } const jane = new person('jane', 'Doe'); jane.greet(); // 输出自身的greet: Doe jane // 如果想强制访问原型上的方法,可以跳过自身属性 console.log(jane.__proto__.greet.call(jane)); // 输出原型的greet: Doe jane
如果你想让所有实例共享原型上的greet方法(这也是原型继承的常用场景),只需要不在构造函数里定义自身的greet,仅在原型上定义即可:
function person(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; } person.prototype.greet = function() { console.log('hey there', this.lastname + ' ' + this.firstname); } const jane = new person('jane', 'Doe'); jane.greet(); // 正常输出:hey there Doe jane
总结
- 函数声明会自动提升,同名函数后定义的会覆盖先定义的,尽量避免在同一作用域重复定义同名构造函数
- 对象属性查找优先自身,再原型链,自身方法不会被原型同名方法覆盖
- 原型继承的核心价值是让多个实例共享方法/属性,减少内存开销
内容的提问来源于stack exchange,提问作者IrfanAnwar
相关产品推荐
相关产品推荐

