示例代码:
 

function Person(name) {
    this.name = name;
}

Person.prototype.sayHello = function() {
    console.log("Hello, my name is " + this.name);
};

let alice = new Person("Alice");
alice.sayHello(); // 输出: Hello, my name is Alice

第一步:回顾构造函数 Person

function Person(name) {
  this.name = name; // 给实例对象添加 name 属性
}

这是一个构造函数(按规范首字母大写),作用是:通过 new 关键字创建「人」的实例对象时,给对象添加 name 属性。

比如 let alice = new Person("Alice"); 会创建一个 alice 对象,结构是:

{
  name: "Alice" // 由构造函数中的 this.name = name 赋值
}

第二步:关键概念 ——prototype(原型)

每个构造函数(比如这里的 Person)都自带一个 prototype 属性,它是一个对象(可以理解为「共享仓库」)。我们可以把「所有实例都需要用到的方法 / 属性」放到这个 prototype 对象里,这样:所有通过这个构造函数创建的实例,都能共享这些方法 / 属性,不用每个实例单独保存一份

第三步:给原型添加方法 sayHello

Person.prototype.sayHello = function() {
  console.log("Hello, my name is " + this.name);
};

这行代码的意思是:给 Person 构造函数的「共享仓库」(prototype)添加一个 sayHello 方法

这个方法的作用是:打印一句包含 name 的问候语,其中 this 指向「调用这个方法的实例对象」(比如后面的 alice)。

第四步:实例对象调用原型上的方法

alice.sayHello(); // 输出: Hello, my name is Alice

当 alice 调用 sayHello() 时,JavaScript 会做两件事:

  1. 先看 alice 自己有没有 sayHello 方法(显然没有,因为 alice 只有 name 属性)。
  2. 找不到就去「创建它的构造函数的原型」(Person.prototype)里找 —— 正好有 sayHello 方法,于是执行。

执行时,方法里的 this 指向 alice,所以 this.name 就是 alice.name(即 "Alice"),最终输出预期结果。

为什么要这么做?(原型的好处)

假设我们用构造函数创建 100 个 Person 实例,如果把 sayHello 写在构造函数里(每个实例都复制一份):

// 不好的写法:每个实例都有自己的 sayHello 方法
function Person(name) {
  this.name = name;
  this.sayHello = function() { ... }; // 每个实例都复制一份,浪费内存
}

100 个实例就会有 100 个一模一样的 sayHello 方法,非常浪费内存。

而用原型的方式,sayHello 只在 Person.prototype 里保存一份,所有实例共享这一份方法,既节省内存又方便统一修改(改一次,所有实例都生效)。

总结

这段代码的逻辑是:

  1. 用 Person 构造函数创建带 name 属性的实例对象(如 alice)。
  2. 把所有实例都需要的 sayHello 方法放到 Person.prototype 里(共享)。
  3. 实例(alice)调用 sayHello 时,会自动去原型上找到并执行,this 指向实例自身,从而正确访问 name

核心是理解:原型是构造函数的「共享仓库」,让所有实例共享方法 / 属性,实现代码复用和内存优化

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐