JavaScript类方法通过原型链查找实现,而非复制给每个实例;方法必须定义在prototype上才能共享,构造函数内定义会浪费内存;class本质是语法糖,静态方法不在原型链上;链式调用需要方法返回this。

谈到JavaScript类方法能够被实例调用,底层依赖的是一套“向上查找”的机制——方法并未复制给实例,而是实例在自身属性中找不到时,沿着原型链逐级向上搜索,最终在原型对象上找到对应方法。这种设计既节省内存又优雅高效,值得深入理解。
方法必须定义在prototype上
只有挂载到构造函数(或class)的prototype属性上的方法,才会进入共享原型链,所有实例共用同一份方法。反之,如果将方法直接定义在构造函数内部,则每个实例都会独立创建该方法,每次新建都会重新分配内存,造成浪费。
- 正确实现方式(共享、省内存):
Person.prototype.say = function() { console.log(this.name); }; - 错误实现方式(不共享、浪费内存):
function Person(name) { this.say = function() { console.log(name); }; }
实例调用方法时的查找过程
当你执行alice.say()时,JavaScript引擎会依次进行三个查找步骤:
- 首先检查
alice自身是否拥有say属性——未找到。 - 接着检查
alice.__proto__(即Person.prototype),这里找到了say方法。 - 执行该方法时,
this自动指向alice,因此alice.name可以正常访问。
class语法下的原型链实现
ES6的class本质是语法糖,底层依然基于原型机制。例如:
class Person { say() { console.log(this.name); } }等同于将say方法直接添加到Person.prototype上。- 通过
new Person('Tom')创建的实例,其__proto__始终指向Person.prototype,与传统构造函数写法完全一致。 - 静态方法(使用
static关键字定义)则直接挂载在构造函数上,不会进入原型链,因此实例无法直接调用——这一点容易忽略,值得特别注意。
链式调用需要返回this
要实现obj.method1().method2()这种流畅的链式调用,每个方法必须显式返回this。只有将当前实例返回,下一次调用才能继续作用于同一对象。
Person.prototype.setName = function(name) { this.name = name; return this; };- 这样,
alice.setName('Alice').say()就能一次性连续执行。
