面试题答案
一键面试利用原型链特性优化函数属性访问和操作
- 理解原型链:
- 在JavaScript中,每个函数都有一个
prototype
属性,它是一个对象,这个对象默认有一个constructor
属性指向该函数本身。当使用构造函数创建实例时,实例的内部会有一个[[Prototype]]
(在现代JavaScript中可以通过__proto__
访问,不推荐直接使用)指向构造函数的prototype
。 - 当访问实例的属性时,如果实例本身没有该属性,JavaScript会沿着原型链向上查找,直到找到该属性或者到达原型链的顶端(
Object.prototype
,其[[Prototype]]
为null
)。
- 在JavaScript中,每个函数都有一个
- 优化属性访问:
- 共享属性:将多个实例共享的属性和方法定义在原型上。例如,对于一个
Person
构造函数,所有Person
实例都可能需要的sayHello
方法,可以定义在Person.prototype
上。
function Person(name) { this.name = name; } Person.prototype.sayHello = function() { console.log(`Hello, I'm ${this.name}`); }; const person1 = new Person('Alice'); const person2 = new Person('Bob'); person1.sayHello();// Hello, I'm Alice person2.sayHello();// Hello, I'm Bob
- 这样,
sayHello
方法只在Person.prototype
中存在一份,而不是每个实例都有自己的sayHello
方法,节省了内存空间,同时在访问sayHello
方法时,由于原型链查找机制,能够快速定位到该方法。
- 共享属性:将多个实例共享的属性和方法定义在原型上。例如,对于一个
- 优化属性操作:
- 避免重复创建方法:如果在构造函数内部定义方法,每个实例都会有自己独立的一份方法副本,这会浪费内存。例如:
function Person(name) { this.name = name; this.sayHello = function() { console.log(`Hello, I'm ${this.name}`); }; } const person1 = new Person('Alice'); const person2 = new Person('Bob');
- 这里
person1.sayHello
和person2.sayHello
是不同的函数实例,占用更多内存。通过将方法定义在原型上,可以避免这种情况。
性能陷阱及避免方法
- 性能陷阱:
- 原型链过长:原型链查找是一个线性过程,如果原型链过长,查找属性的时间会增加。例如:
function A() {} function B() {} function C() {} function D() {} B.prototype = new A(); C.prototype = new B(); D.prototype = new C(); const d = new D(); // 当访问d的某个属性时,如果d和D.prototype都没有该属性,会沿着A -> B -> C -> D的原型链查找,链条过长可能影响性能
- 频繁的属性查找:如果在循环等频繁执行的代码块中进行属性查找,每次查找都可能涉及原型链查找,尤其是当属性不在实例本身时,会降低性能。例如:
function Person() {} Person.prototype.age = 30; const person = new Person(); for (let i = 0; i < 1000000; i++) { // 这里每次循环都要查找person.age,虽然age在原型上,但频繁查找仍可能影响性能 console.log(person.age); }
- 避免陷阱的代码示例:
- 缩短原型链:尽量减少不必要的原型继承层次。例如,如果
D
只需要A
的部分功能,可以直接将D.prototype
设置为Object.create(A.prototype)
,这样可以减少中间B
和C
的层次。
function A() {} function D() {} D.prototype = Object.create(A.prototype); const d = new D();
- 缓存属性:对于频繁访问的属性,如果它在原型链上,可以将其缓存到实例上。例如:
function Person() {} Person.prototype.age = 30; const person = new Person(); const cachedAge = person.age; for (let i = 0; i < 1000000; i++) { // 这里访问cachedAge,避免了频繁的原型链查找 console.log(cachedAge); }
- 缩短原型链:尽量减少不必要的原型继承层次。例如,如果