JavaScript继承:别让老爸的咖啡杯被你喝空了!

56 阅读3分钟

JavaScript继承:别让老爸的咖啡杯被你喝空了!

🎭 一个小故事

小明想"继承"他老爸的咖啡杯,于是写了:

Son.prototype = Father.prototype;

结果?他老爸刚倒完咖啡,小明的杯子也空了!
为什么?因为小明继承的不是咖啡杯的"副本",而是"同一个杯子"!
核心问题:JavaScript的继承是"引用",不是"复制"!


🔥 三种继承方式,只有一种能用(副作用大揭秘)

❌ 1. 直接继承原型(最坑!)

function Father() {}
Father.prototype.coffee = "星巴克";

function Son() {}
Son.prototype = Father.prototype; // 直接继承

// 修改Son的原型
Son.prototype.coffee = "麦当劳";

console.log(Father.prototype.coffee); // 输出: "麦当劳" ❌ !!

副作用:修改Son的原型,Father的原型也被改了!
就像小明的"继承" :老爸刚倒完咖啡,小明的杯子也空了——因为它们根本是同一个杯子!


⚠️ 2. 组合继承(有小坑)

function Father(name) {
  this.name = name;
}
Father.prototype.sayHello = function() { console.log("Hello " + this.name); };

function Son(name, age) {
  Father.call(this, name); // 借用构造函数
  this.age = age;
}
Son.prototype = new Father(); // 原型链继承
Son.prototype.constructor = Son;

// 父类构造函数被调用了两次!

副作用:父类构造函数被调用了两次,浪费性能!
就像小明:老爸先给他倒了杯咖啡(第一次调用),然后又给他整了个新杯子(第二次调用)——多喝了一杯,浪费!

📹小剧场

小明:老爸,什么是原型啊?

老爸:原型都不知道?还不去看你晴栀哥的文章!(朝着小明甩了一个链接)


✅ 3. 寄生组合继承(最优方案)

💡 为什么它这么牛?

  • 空函数当"中间人"
  • 不污染父类原型
  • 不重复调用父类构造函数
function inherit(child, parent) {
  const temp = function() {}; // 空函数!
  temp.prototype = parent.prototype;
  child.prototype = new temp();
  child.prototype.constructor = child;
}

🔍 关键原理(不污染父类的秘诀)

  • temp是空函数,它的实例不会影响temp的原型
  • temp.prototype = parent.prototype → 让中介指向父类原型
  • child.prototype = new temp() → 子类原型 = 中介实例
  • 所以child.__proto__ → child.prototype → parent.prototype → Object.prototype

副作用?不存在的! 父类原型一点没被污染。


💼 面试官问:"手写继承"?直接甩出这个:

function inherit(child, parent) {
  const temp = function() {};
  temp.prototype = parent.prototype;
  child.prototype = new temp();
  child.prototype.constructor = child;
}

为什么这是标准答案?

  • 解决了直接继承的副作用(不污染父类)
  • 避免了组合继承的重复调用
  • 是目前最优雅、最常用的继承方式

🧠 一句话总结继承哲学

继承方式像什么问题解决方案
直接继承借了老爸的咖啡杯老爸倒咖啡,你杯子空了❌ 不能用
组合继承老爸倒了两杯咖啡多喝了一杯,浪费!⚠️ 有小坑
空函数寄生组合继承用空杯子当中介,完美复制不污染老爸的杯子!✅ 面试必用!

💡 小明的顿悟
"继承不是复制,而是优雅的引用。用空函数寄生组合继承,我既能拥有老爸的咖啡杯,又不会让他喝不到咖啡!"


现在,轮到你了——面试官问"手写继承",你直接甩出这个代码,然后说:
"这个方案解决了直接继承的副作用,避免了组合继承的重复调用,是目前最优雅的继承方式。"