JS 面向对象其实很简单:跟着加菲猫一起读懂构造函数与原型链 🐱

71 阅读4分钟

很多初学者一听“面向对象 OOP”,脑袋直接嗡的一声 ——
类?继承?实例?构造函数?prototype?原型链???

但其实只需要跟着 加菲猫 学一遍,你就会发现:

JavaScript 的对象系统其实一点都不复杂,它只是跟加菲猫一样 —— 看起来慵懒,背后却有一套自己的“独门体系”。

今天我们一起从最轻松的方式重新理解 JS 的对象模型:
从创建一只猫开始 🐱


一、对象字面量:做一只“手搓”的加菲猫

最开始的加菲猫,当然是一个字面量对象:

const cat = { 
  name: '加菲猫', 
  color: '橘色' 
};

很简单对吧?
但如果你需要 一百只猫 呢?

const cat1 = { ... };
const cat2 = { ... };
const cat3 = { ... };

写到你想变成猫。

🐱 加菲:我建议你写个机器,不要真的手搓 100 次。

于是我们开始需要“封装”。


二、构造函数:开一家“猫咪加工厂” 🏭

构造函数就是一个 能生产实例的工厂

function Cat(name, color) {
  this.name = name;
  this.color = color;
}

用它制造猫:

const cat1 = new Cat('加菲猫', '橘色');
const cat2 = new Cat('黑猫警长', '黑色');

new 这个小东西非常关键,JS 会为我们偷偷做四件事:

  1. 创建一个空对象 {}
  2. 将这个对象绑定给 this
  3. 执行构造函数,把属性都挂到这个新对象上
  4. 返回这个对象

所以:

const cat1 = new Cat('tom', '黑色');

就等于 👇
“生成一只叫 tom 的黑色猫”。


三、问题出现:每只猫都复制一套方法,太胖了🐱💥

假设你在构造函数里写方法:

function Cat(name, color) {
  this.name = name;
  this.color = color;
  this.eat = function() {
    console.log("吃 jerry");
  }
}

每 new 一只猫,就复制一份 eat(),100 只猫就 100 份,
JS 内存都快被猫咪吃光了。

🐱 加菲:我很胖,但代码不能跟我一样胖。

于是,JS 给你准备了“共享仓库”—— prototype


四、prototype:所有猫共享的大仓库 🏬

prototype 的作用是:

放所有 不会改变、可以共享 的方法。

改写如下:

function Cat(name, color) {
  this.name = name;
  this.color = color;
}

Cat.prototype.eat = function() {
  console.log('吃 jerry');
};

Cat.prototype.type = '猫科动物';

现在:

  • 每只猫自己的属性:name/color
  • 所有猫共享的方法:eat / type

检验一下:

cat1.hasOwnProperty('type') // false
cat1.type // 从原型里找

🐱 加菲:我自己的东西我自己保存,共同的东西放在仓库里,大家共享多合理。


五、原型链:猫咪的“家谱系统” 🧬

当你访问 cat1.eat,JS 会:

  1. 先看“自己家”有没有(实例)
  2. 没有就去找“父亲”(prototype)
  3. 再没有继续往上一代(Object.prototype)
  4. 最后到 null

这条查找路线,就是 原型链 prototype chain

如果把对象想象成猫咪:

cat1 → Cat.prototypeAnimal.prototypeObject.prototypenull

每一代都可能存着猫咪的祖传本领。


六、继承:让猫继承动物的属性 🐱 → 🐯

先来动物类:

function Animal() {
  this.species = '动物';
}

Animal.prototype.sayHi = function() {
  console.log('哪哪哪啦');
}

让 Cat 继承 Animal 的属性:

function Cat(name, color) {
  // 继承实例属性
  Animal.apply(this);
  this.name = name;
  this.color = color;
}

然后让 Cat 继承 Animal 的方法:

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;

现在 cat 就是一只名副其实的动物:

const cat = new Cat('加菲猫', '橘色');

cat.species  // 动物
cat.sayHi() // 哪哪哪啦

🐱 加菲:谢谢你让我终于在户口本上变成“动物”了。


七、ES6 class:更好看的“猫咪工厂”包装纸 🎁

ES6 的 class 并没有改变 JS 本质(底层还是原型),但写起来更开心:

class Cat {
  constructor(name, color) {
    this.name = name;
    this.color = color;
  }

  eat() {
    console.log('吃 jerry');
  }
}

继承更简单:

class Animal {
  constructor() {
    this.species = '动物';
  }

  sayHi() {
    console.log('哪哪哪啦');
  }
}

class Cat extends Animal {
  constructor(name, color) {
    super(); // 等同于 Animal.apply(this)
    this.name = name;
    this.color = color;
  }
}

🐱 加菲:穿上 ES6 的新衣服,我更帅了,但骨子里还是那只加菲猫。


八、掌握 JS OOP,只需要记住这套“加菲猫口诀”🐱📜

🌟 口诀 1:构造函数是工厂

new 会自动创建对象 + 绑定 this + 初始化属性。

🌟 口诀 2:共有的放 prototype

不变的、可复用的都放到 prototype,省内存。

🌟 口诀 3:原型链是家谱

查属性顺序:实例 → 原型 → 原型的原型 → ... → null。

🌟 口诀 4:继承分两步

  • 继承属性:apply / super()
  • 继承方法:改变子类的 prototype 指向

🌟 口诀 5:class 只是语法糖

背后还是构造函数 + prototype。


🎉 最后:JS 的 OOP,其实没那么难

只要你理解:

  • 构造函数是生产猫的工厂
  • prototype 是共享仓库
  • 原型链是猫咪的家谱
  • 继承是把父类的属性方法嫁接过来

你就已经真正掌握了 JavaScript 的 OOP 核心。