JavaScript 原型与原型链详情
在 JavaScript 中,每个对象都拥有一个原型(prototype)属性。原型是一个对象,它包含了创建当前对象的构造函数的原型。当你访问一个对象的属性时,JavaScript 引擎会先在该对象本身中查找是否有这个属性,如果没有,它会去该对象原型(也就是构造函数的原型)中查找是否有这个属性,如果还没有,就会继续在原型的原型中查找,直到找到该属性或者到达 Object 的原型为止。这种属性查找的链式结构就是原型链。
构造函数与实例
在 JavaScript 中,每个函数既可以作为普通函数调用,也可以作为构造函数使用。当函数作为构造函数使用时,它们会返回一个新创建的对象。我们可以使用 new 运算符来创建对象,例如:
function Person(name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person('小明', 18);
var person2 = new Person('小红', 20);
console.log(person1.name); // 小明
console.log(person2.age); // 20
在上面的代码中,我们通过构造函数 Person 创建了两个对象 person1 和 person2。由于 person1 和 person2 都是使用 Person 构造函数创建的,所以它们都具有 name 和 age 两个属性。
原型和原型链
当我们使用构造函数创建对象时,JavaScript 引擎会自动为这个对象创建一个原型属性 prototype,它指向该对象的构造函数的原型属性。假设我们现在有两个对象 person1 和 person2,它们都是使用 Person 构造函数创建的,那么它们的原型属性 prototype 就会指向如下对象:
Person.prototype = {
constructor: Person,
sayName: function() {
console.log(this.name);
}
};
所以,当我们在 person1 或者 person2 对象中查找 name 属性时,如果该对象本身没有这个属性的话,JavaScript 引擎就会在该对象原型(也就是 Person.prototype 对象)中查找是否有该属性。如果该属性在 Person.prototype 对象中存在,JavaScript 引擎就会把该属性返回给我们。
如果 person1 或者 person2 对象中仍然没有找到该属性,JavaScript 引擎就会在 Person.prototype 的原型中继续查找。由于 Person.prototype 是一个对象,它也有一个原型属性,这个原型属性指向它的构造函数 Object.prototype。最后,如果在 Object.prototype 中还没有找到该属性,JavaScript 引擎就会返回 undefined。
实例与原型的关系
我们知道,当我们使用 new 运算符创建对象时,JavaScript 引擎会为该对象创建一个原型属性 prototype,这个属性与构造函数的原型属性相等(Person.prototype)。同时,JavaScript 引擎还会把该对象的 proto 属性指向该对象原型属性(也就是 Person.prototype)。例如:
function Person(name, age) {
this.name = name;
this.age = age;
}
var person1 = new Person('小明', 18);
console.log(person1.__proto__ === Person.prototype); // true
上面的代码中,我们创建了一个 Person 对象 person1,然后我们可以通过 person1.proto 访问该对象的原型属性。由于 person1 是通过 Person 构造函数创建的,所以 person1.proto 指向的就是 Person.prototype。
同时,我们也知道,当我们在 person1 对象中查找 name 属性时,如果该对象本身没有这个属性的话,JavaScript 引擎就会在该对象原型(也就是 Person.prototype 对象)中查找是否有该属性。
示例
以下是使用构造函数创建对象、原型和原型链的两个示例:
示例一
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(this.name + '正在吃东西');
};
function Cat(name) {
Animal.call(this, name);
this.type = 'cat';
}
Cat.prototype = Object.create(Animal.prototype, {
constructor: {
value: Cat,
enumerable: false,
writable: true,
configurable: true
}
});
Cat.prototype.miao = function() {
console.log(this.name + '正在喵喵叫');
};
var cat = new Cat('小猫');
cat.miao(); // 小猫正在喵喵叫
cat.eat(); // 小猫正在吃东西
在上面的代码中,我们定义了两个构造函数 Animal 和 Cat。Animal 构造函数用于创建动物对象,Cat 构造函数用于创建猫对象。我们通过 Object.create 方法将 Cat 的原型属性指向了 Animal 的原型属性,这样 Cat 就继承了 Animal 的所有属性和方法。
Cat 构造函数有一个 miao 方法,用于输出猫正在喵喵叫的信息。Animal 构造函数有一个 eat 方法,用于输出动物正在吃东西的信息。我们通过 Cat 对象 cat 调用 miao 和 eat 方法,发现它们都能正常输出信息。
示例二
function Car(name, price) {
this.name = name;
this.price = price;
}
Car.prototype.run = function() {
console.log(this.name + '正在行驶');
};
function Audi(name, price) {
Car.call(this, name, price);
this.type = 'SUV';
}
Audi.prototype = new Car();
var audi = new Audi('Audi Q5', '50万');
audi.run(); // Audi Q5正在行驶
console.log(audi.price); // 50万
在上面的代码中,我们定义了两个构造函数 Car 和 Audi。Car 构造函数用于创建汽车对象,Audi 构造函数用于创建奥迪对象。
我们通过 Car.call(this, name, price) 将 Audi 构造函数的作用域指向了 Car,这样我们就可以在 Audi 对象中调用 Car 构造函数中的属性和方法。
接下来,我们通过 Audi.prototype = new Car() 将 Audi 对象的原型指向了 Car 对象。这样一来,Audi 就继承了 Car 的所有属性和方法。
最后,我们创建了一个 Audi 对象 audi,并通过 audi.run() 方法输出 Audi Q5 正在行驶的信息。我们还通过 console.log(audi.price) 输出了 Audi Q5 的价格。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript 原型与原型链详情 - Python技术站