JavaScript prototype(原型对象)
学习 JavaScript prototype(原型对象) 是非常重要的一部分,它帮助你理解 JavaScript 中对象和继承的机制。原型是每个 JavaScript 对象的隐式属性,它指向一个对象,这个对象包含了该对象的共享属性和方法。
1. 原型链与继承机制
原型链
每个对象都有一个 __proto__ 属性,指向它的原型对象。原型对象本身也有自己的 __proto__,直到最终指向 Object.prototype,这是原型链的最顶层。
例如,创建一个对象并检查它的原型链:
const obj = { name: "Alice" };
console.log(obj.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
obj的__proto__指向Object.prototype。Object.prototype.__proto__是null,表示原型链的终点。
原型链的继承
通过原型链,子对象可以继承父对象的属性和方法。来看一个例子:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name + " makes a sound.");
};
function Dog(name) {
Animal.call(this, name); // 调用父类构造函数
}
Dog.prototype = Object.create(Animal.prototype); // 继承Animal的方法
Dog.prototype.constructor = Dog; // 修正构造函数指向
const dog = new Dog('Buddy');
dog.speak(); // 输出:Buddy makes a sound.
解释:
Dog.prototype = Object.create(Animal.prototype)使得Dog的实例能够访问Animal中的方法。Dog.prototype.constructor = Dog用来修正Dog构造函数的指向,因为继承时会修改constructor。
2. Prototype 属性与实例化
prototype 属性
每个函数(构造函数)都有一个 prototype 属性,它指向一个对象,这个对象包含该构造函数实例的共享方法。
例如:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log("Hello, " + this.name);
};
const person1 = new Person("Alice");
const person2 = new Person("Bob");
person1.sayHello(); // Hello, Alice
person2.sayHello(); // Hello, Bob
console.log(person1.__proto__ === Person.prototype); // true
解释:
Person.prototype存储的是sayHello方法,它是所有Person实例共享的。person1.__proto__和person2.__proto__都指向Person.prototype。
创建实例时如何访问原型
function Car(make, model) {
this.make = make;
this.model = model;
}
Car.prototype.drive = function() {
console.log(this.make + " " + this.model + " is driving.");
};
const car1 = new Car("Toyota", "Corolla");
const car2 = new Car("Honda", "Civic");
car1.drive(); // Toyota Corolla is driving.
car2.drive(); // Honda Civic is driving.
重点:
drive方法是通过Car.prototype来共享的。- 每个实例有自己的属性(
make,model),但是它们共享drive方法。
3. 原型方法和继承
通过原型添加方法
我们可以通过原型为所有实例添加方法。例如,为 Person 类添加一个 introduce 方法:
Person.prototype.introduce = function() {
console.log("Hi, I'm " + this.name);
};
const person3 = new Person("Charlie");
person3.introduce(); // Hi, I'm Charlie
原型链的优势:
- 所有
Person类的实例都可以访问introduce方法,而不需要每个实例都拥有一个副本。
继承父类原型方法
我们可以通过 Object.create 继承父类的原型方法:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name + " makes a sound.");
};
function Cat(name) {
Animal.call(this, name); // 调用父类构造函数
}
Cat.prototype = Object.create(Animal.prototype); // 继承父类原型方法
Cat.prototype.constructor = Cat; // 修正构造函数
const cat1 = new Cat("Whiskers");
cat1.speak(); // Whiskers makes a sound.
解释:
Object.create(Animal.prototype)创建一个新的对象作为Cat.prototype,它继承了Animal.prototype上的方法。Cat.prototype.constructor = Cat用于确保Cat实例的构造函数是Cat。
4. 修改原型链
你可以修改对象的原型链,给对象动态添加方法或属性:
const person4 = new Person("David");
person4.__proto__.sayGoodbye = function() {
console.log(this.name + " says goodbye.");
};
person4.sayGoodbye(); // David says goodbye.
注意:
- 这种修改会影响所有继承该原型链的实例。
5. 实践案例:创建一个动物类和子类
让我们创建一个动物类,并添加不同类型的动物:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name + " makes a sound.");
};
function Dog(name) {
Animal.call(this, name); // 调用父类构造函数
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log(this.name + " barks.");
};
function Cat(name) {
Animal.call(this, name); // 调用父类构造函数
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.meow = function() {
console.log(this.name + " meows.");
};
// 创建实例
const dog = new Dog("Buddy");
const cat = new Cat("Whiskers");
dog.speak(); // Buddy makes a sound.
dog.bark(); // Buddy barks.
cat.speak(); // Whiskers makes a sound.
cat.meow(); // Whiskers meows.
总结:
Dog和Cat继承了Animal的speak方法,但各自有自己独特的方法(bark和meow)。- 这种方式实现了简单的继承关系。
更多详细内容请关注其他文章!