引言
嘿,你还记得小时候玩过的积木吗?你可以用同样的几块积木搭建出无数种造型。JavaScript 中的“原型”和“继承”有点类似,它们让你能够用少量的代码创建许多不同的对象。这篇文章就像是一场积木大赛,带你一起看看如何用原型和继承搭建出各种有趣的代码结构。
1. 原型是什么?
你可以把原型想象成一块模板积木。每个 JavaScript 对象都有一个“原型”,它是用来提供那些我们不想重复造的轮子。
1.1 给你打个比方
想象你是个厨师,每次做菜都要从头准备调料、火候、时间等基本步骤。累吧?这时候你有了一个菜谱,这个菜谱就是“原型”。你按照菜谱(原型)去做,省了不少事。
在 JavaScript 中,原型就是对象的菜谱,里面有对象的基本属性和方法。当你创建一个新的对象时,这个新对象会“继承”它的原型中的方法和属性,就像你按照菜谱做菜一样。
示例:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello, my name is ${this.name}.`);
};
const alice = new Person("Alice");
alice.greet(); // 输出:Hello, my name is Alice.
在这个例子里,Person
就是你的“菜谱”,而 greet
方法是“做菜的步骤”。每次你创建一个新的人物对象,它都会继承这个步骤。
1.2 那原型链是什么呢?
如果原型是菜谱,那原型链就是一系列菜谱的连接。假设你有一个总的菜谱,里面有各种基础菜肴的做法,然后你可以有其他更详细的菜谱,继承这个基础菜谱。原型链就是这么运作的。
比喻:
- 基础菜谱:Object 原型
- 详细菜谱:你定义的原型
每当你调用一个对象的方法时,JavaScript 会顺着原型链往上找,就像你找不到做某道菜的方法时,会去翻基础菜谱一样。
2. 继承是怎么回事?
继承让你可以用现有的积木块(原型)搭建出新玩意儿,而不必从头开始。
2.1 假设你是玩具设计师
你设计了一款基础玩具车,具有轮子和车身。接着,你想设计一款更酷的赛车,你当然不想重新造轮子吧?所以你决定“继承”基础玩具车的设计,加上一些新元素,比如涡轮增压。
示例:
function Car(make, model) {
this.make = make;
this.model = model;
}
Car.prototype.startEngine = function () {
console.log(`${this.make} ${this.model} engine started.`);
};
function SportsCar(make, model, turbo) {
Car.call(this, make, model); // 继承 Car 的属性
this.turbo = turbo;
}
// 继承 Car 的方法
SportsCar.prototype = Object.create(Car.prototype);
SportsCar.prototype.constructor = SportsCar;
SportsCar.prototype.boost = function () {
console.log(`${this.make} ${this.model} boosting with ${this.turbo}!`);
};
const myCar = new SportsCar("Porsche", "911", "Turbo S");
myCar.startEngine(); // 输出:Porsche 911 engine started.
myCar.boost(); // 输出:Porsche 911 boosting with Turbo S!
在这个例子里,SportsCar
就像是在基础玩具车的设计上加了涡轮增压,通过继承基础设计实现了一个更复杂的玩具。
2.2 原型继承的好处
继承不仅让你节省代码,还让你的代码更加模块化和易于维护。如果你需要改进玩具车的设计,只需修改基础设计,所有继承它的赛车也会自动获得这些改进。
比喻:
就像如果你改进了菜谱中的某个基础步骤,所有依赖这个步骤的菜都会变得更好吃。
3. 常见的继承模式
在 JavaScript 中,有几种常见的继承模式,接下来我们一起看看。
3.1 原型链继承
这是最简单的继承方式,你只需要让新对象的原型指向父对象的实例。
示例:
function Animal() {
this.species = "animal";
}
Animal.prototype.speak = function () {
console.log(`A ${this.species} makes a sound.`);
};
function Dog(name) {
this.name = name;
}
Dog.prototype = new Animal(); // Dog 继承 Animal
Dog.prototype.bark = function () {
console.log(`${this.name} barks!`);
};
const rex = new Dog("Rex");
rex.speak(); // 输出:A animal makes a sound.
rex.bark(); // 输出:Rex barks!
注意: 原型链继承虽然简单,但它也有一些问题,比如当父对象的属性是引用类型时,子对象会共享这个属性,这可能导致一些意外的错误。
3.2 组合继承
组合继承结合了原型链继承和构造函数继承的优点,解决了原型链继承的共享属性问题。
示例:
function Animal(species) {
this.species = species;
}
Animal.prototype.speak = function () {
console.log(`A ${this.species} makes a sound.`);
};
function Dog(name) {
Animal.call(this, "dog"); // 继承 Animal 的属性
this.name = name;
}
Dog.prototype = Object.create(Animal.prototype); // 继承 Animal 的方法
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function () {
console.log(`${this.name} barks!`);
};
const rex = new Dog("Rex");
rex.speak(); // 输出:A dog makes a sound.
rex.bark(); // 输出:Rex barks!
这种继承方式确保了每个子对象有自己的属性,同时还能继承父对象的所有方法。
3.3 ES6 class 语法
如果你觉得上面的继承方式有点麻烦,别担心,ES6 提供了更简洁的 class
语法,让继承变得更加直观。
示例:
class Animal {
constructor(species) {
this.species = species;
}
speak() {
console.log(`A ${this.species} makes a sound.`);
}
}
class Dog extends Animal {
constructor(name) {
super("dog"); // 调用父类的构造函数
this.name = name;
}
bark() {
console.log(`${this.name} barks!`);
}
}
const rex = new Dog("Rex");
rex.speak(); // 输出:A dog makes a sound.
rex.bark(); // 输出:Rex barks!
使用 class
语法,就像是在搭建乐高积木,结构清晰,操作简便。
结论
你已经学会了如何在 JavaScript 中用原型和继承来搭建你的代码结构。这就像是用积木搭建模型,通过基础的积木块(原型),你可以构建出丰富多样的复杂结构(对象)。
社区与资源
想进一步深入了解 JavaScript 的原型和继承吗?以下是一些推荐的资源:
- MDN Web Docs - Inheritance and the prototype chain - 原型和继承的权威文档。
- JavaScript.info - Prototypes - 详细解释 JavaScript 原型的资料。
- Stack Overflow - 解决 JavaScript 编程问题的社区。
1 条评论
rkjeuo84362WO-广告投放合理,虽然有广告,但不影响阅读体验,整体感受很好。http://www.pandalearn.cn//