面试题答案
一键面试原型链概念
在 JavaScript 中,每个对象都有一个 [[Prototype]]
内部属性(在现代 JavaScript 中可以通过 __proto__
访问,虽然 __proto__
已不推荐直接使用,但为了便于理解可以这么看),这个属性指向该对象的原型对象。原型对象本身也是一个对象,它也有自己的 [[Prototype]]
,以此类推形成一条链式结构,直到原型链的末端(通常是 null
),这条链式结构就被称为原型链。
当访问一个对象的属性或方法时,如果该对象自身没有定义这个属性或方法,JavaScript 引擎就会沿着原型链向上查找,直到找到该属性或方法,或者到达原型链的末端(null
)。如果到达 null
仍未找到,则返回 undefined
。
原型链在类和原型性能优化方面的作用
- 避免重复定义:通过原型链,多个对象实例可以共享原型对象上的属性和方法,而不必在每个实例上重复创建,从而节省内存空间,提升性能。例如,在模拟类的继承场景中,子类实例可以通过原型链访问到父类原型上定义的方法,而无需在子类每个实例中都重新定义这些方法。
- 代码复用:有利于代码复用,减少冗余代码。将通用的属性和方法定义在原型上,所有基于该原型创建的对象都能受益,提高开发效率。
举例说明如何合理利用原型链提升性能
// 定义一个构造函数
function Animal(name) {
this.name = name;
}
// 在原型上定义方法
Animal.prototype.speak = function() {
console.log(this.name +'makes a sound.');
};
// 定义一个 Dog 构造函数,继承自 Animal
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
// 设置 Dog 的原型为 Animal 的实例,建立原型链
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 在 Dog 原型上定义特有的方法
Dog.prototype.bark = function() {
console.log(this.name +'barks.');
};
// 创建两个 Dog 实例
let dog1 = new Dog('Buddy', 'Golden Retriever');
let dog2 = new Dog('Max', 'Labrador');
// 两个实例共享 Animal 原型上的 speak 方法,而不是各自拥有一份
dog1.speak(); // Buddy makes a sound.
dog2.speak(); // Max makes a sound.
在上述例子中,Animal
构造函数的 speak
方法定义在原型上,Dog
构造函数通过原型链继承了 Animal
原型上的 speak
方法。dog1
和 dog2
这两个 Dog
实例无需重复定义 speak
方法,而是通过原型链共享该方法,节省了内存,提升了性能。