You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何对象自有同名函数添加原型函数后无法访问?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中,函数声明会被提升到当前作用域的最顶端,而且如果有同名的函数声明,后面的会直接覆盖前面的。也就是说,你的代码实际执行顺序是:

  1. 先执行第二个person函数的声明(直接覆盖第一个)
  2. 再执行第一段的实例创建代码——这时候调用的是没有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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:16:49