06. JavaScript 原型链原理详解
1. 构造函数与原型对象的关系
我们知道,对于 JS 创建对象有两种方式:
- 过程式创建,就像上面一样,使用
{}
包围键值对来创建 - 面向对象编程,使用包含
this
的函数作为类创建器
对于 ES6+,class
语法糖可以代替上述面向对象的创建方式,这种方式的本质仍然是第二种方式。
设函数为 f
,由此函数 new
所创建的三个对象 a1
、a2
、a3
。
prototype
是每个函数都拥有的属性,这个函数创建对象时将使用这个对象作为原型__proto__
和constructor
是a1
、a2
、a3
所拥有的属性__proto__
指向对象的原型对象,原型对象通常和被new
的对象结构一致constructor
指向构造该对象的函数
a1.constructor === f
a1.__proto__ === f.prototype
每个 JavaScript 函数(箭头函数除外)在创建时都会自动获得一个 prototype
属性,该属性指向一个包含 constructor
属性的对象。这种设计实现了实例共享方法的能力。
javascript
function Person(name) {
this.name = name;
}
// 自动创建的原型对象
console.log(Person.prototype.constructor === Person); // true
当使用 new
操作符创建实例时:
javascript
const alice = new Person('Alice');
console.log(alice.__proto__ === Person.prototype); // true
2. 原型链查找机制
属性访问遵循委托查找规则:
- 在实例自身属性中查找
- 未找到时沿
__proto__
链向上查找 - 直到
Object.prototype
(最终为null
)
示例演示:
javascript
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
alice.sayHello(); // 通过原型链调用
console.log(alice.toString()); // 来自 Object.prototype
3. 原型链终点验证
通过递归访问 __proto__
可验证原型链终点:
javascript
function getProtoChain(obj) {
const chain = [];
let current = obj;
while(current) {
chain.push(current.constructor.name);
current = Object.getPrototypeOf(current);
}
return chain;
}
console.log(getProtoChain(alice));
// ["Person", "Object", ...]
4. 原型继承的实现模式
4.1 组合继承(经典继承)
javascript
function Student(name, grade) {
Person.call(this, name);
this.grade = grade;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
const bob = new Student('Bob', 10);
console.log(bob instanceof Person); // true
4.2 ES6 Class 语法糖
javascript
class Teacher extends Person {
constructor(name, subject) {
super(name);
this.subject = subject;
}
}
const mrSmith = new Teacher('Smith', 'Math');
console.log(mrSmith.sayHello()); // 继承自 Person
5. 关键方法对比
方法 | 作用域 | 原型链检查 |
---|---|---|
hasOwnProperty | 仅自身属性 | × |
in 操作符 | 全部属性 | √ |
Object.keys() | 可枚举属性 | × |
Reflect.ownKeys() | 所有属性 | × |
javascript
console.log('toString' in alice); // true
console.log(alice.hasOwnProperty('toString')); // false
6. 性能优化建议
- 避免过长的原型链:保持继承层级在 3-4 级以内
- 谨慎扩展原生原型:可能引发命名冲突
- 优先使用对象组合:而非深层次类继承
javascript
// 不推荐的写法
Array.prototype.customMethod = function() { /*...*/ };
// 建议的替代方案
const arrayUtils = {
customMethod: function(arr) { /*...*/ }
};
7. 现代 JavaScript 的改进
使用 Object.create()
实现纯净原型链:
javascript
const animal = {
eat() {
console.log('Eating...');
}
};
const rabbit = Object.create(animal, {
jump: { value: function() { /*...*/ } }
});
通过原型链理解,我们可以更好地掌握 JavaScript 的面向对象特性,在框架开发、库设计和大型应用架构中做出合理的设计决策。