在 JavaScript 中,对象是通过原型继承的。每个对象都有一个原型对象(prototype),它是一个对象或 null,它定义了该对象的属性和方法。如果我们访问一个对象的属性或方法时,JavaScript 引擎会首先查找该对象自身是否存在该属性或方法,如果不存在,则查找该对象的原型对象是否存在该属性或方法,以此类推,直到最终查找到 Object.prototype,如果还没有找到,则返回 undefined。
原型和原型链是理解 JavaScript 对象模型的重要概念。理解原型和原型链的工作原理可以帮助开发人员更好地设计和优化代码,并避免常见的错误。
原型
在 JavaScript 中,每个函数都有一个特殊的属性叫做原型 (prototype)。原型是一个普通的对象,它包含了方法和属性的定义。当创建一个新的函数时,JavaScript 引擎会自动为该函数创建一个原型对象,并将其关联到该函数的 prototype 属性上。
javascript复制代码
function Person(name) {
this.name = name;
}
console.log(Person.prototype); // {}
在上面的例子中,Person 函数被创建时,JavaScript 引擎同时也创建了一个空对象 {} 并将其赋值给了 Person.prototype。
当使用 new 运算符创建一个实例时,JavaScript 引擎会自动将该实例的 proto 指针指向该函数的原型对象。
javascript复制代码
const john = new Person('John');
console.log(john.__proto__ === Person.prototype); // true
在上面的例子中,我们创建了一个名为 john 的 Person 实例,并检查了它的 proto 指针是否指向了 Person.prototype。结果是 true,这意味着 john 继承了 Person.prototype 的属性和方法。
原型链
原型链是一种用于实现继承的机制,它是由多个对象组成的链式结构,每个对象都有一个指向其父对象的引用。当访问对象的属性或方法时,JavaScript 引擎会沿着原型链依次查找,直到找到该属性或方法,或者查找到 Object.prototype 为止。
假设我们有一个对象 o,它有一个属性 x,我们想要访问 o.x。如果 o 自身没有定义 x 属性,那么 JavaScript 引擎会查看 o.proto 是否存在 x 属性。如果不存在,则会继续查找 o.proto.proto,以此类推,直到找到 x 属性或查找到 Object.prototype 为止。
下面是一个示例:
javascript复制代码
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function() {
console.log(`Hi, my name is ${this.name}.`);
};
function Student(name, major) {
Person.call(this, name);
this.major = major;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.sayMajor = function() {
console.log(`My major is ${this.major}.`);
};
const john = new Student('John', 'Physics');
john.sayHi(); // Hi, my name is John.
john.sayMajor(); // My major is Physics.
在上面的示例中,我们定义了两个构造函数:Person 和 Student。Person 函数定义了一个名为 sayHi 的方法,它打印出该对象的姓名。Student 函数继承了 Person 函数,并定义了一个名为 sayMajor 的方法,它打印出该学生的专业。
我们创建了一个名为 john 的 Student 实例,并调用了其 sayHi 和 sayMajor 方法来验证原型链机制是否正常工作。
总结
在 JavaScript 中,原型和原型链是实现对象继承的重要概念。每个对象都有一个原型对象,它定义了该对象的属性和方法。JavaScript 引擎会在查找对象属性和方法时沿着原型链依次查找,直到找到该属性或方法,或者查找到 Object.prototype 为止。我们可以利用原型和原型链来实现对象的继承和代码的复用。
一些需要注意的点:
- 对象的 proto 属性可以访问和修改该对象的原型对象。
- 函数的 prototype 属性是用于创建该函数的实例时自动关联到该实例的原型对象。
- 在使用 new 运算符创建实例时,JavaScript 引擎会自动将该实例的 proto 指针指向该函数的原型对象。
- 使用 Object.create() 方法可以创建一个新对象,并将其原型指向传入的对象。
- 可以使用 instanceof 运算符检查一个对象是否是某个构造函数的实例。