1. 原型(Prototype)
什么是原型?
每个 JavaScript 对象都有一个隐藏的 [[Prototype]]
属性,它指向另一个对象,这个被指向的对象就是它的"原型"。
如何访问原型?
javascript
// 通过 __proto__ 属性(非标准,但浏览器都支持)
const obj = {};
console.log(obj.__proto__); // 指向 Object.prototype
// 通过 Object.getPrototypeOf()(推荐的标准方式)
console.log(Object.getPrototypeOf(obj)); // 同样指向 Object.prototype
示例:对象的原型
javascript
const person = {
name: 'Alice',
age: 25
};
console.log(person.__proto__ === Object.prototype); // true
2. 构造函数与原型
构造函数创建对象
javascript
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在原型上添加方法
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const alice = new Person('Alice', 25);
const bob = new Person('Bob', 30);
alice.sayHello(); // "Hello, I'm Alice"
bob.sayHello(); // "Hello, I'm Bob"
为什么使用原型?
javascript
// 如果不使用原型,每个实例都会有独立的方法副本
function BadPerson(name) {
this.name = name;
this.sayHello = function() { // ❌ 每个实例都有独立的函数
console.log(`Hello, I'm ${this.name}`);
};
}
// 使用原型,所有实例共享同一个方法
function GoodPerson(name) {
this.name = name;
}
GoodPerson.prototype.sayHello = function() { // ✅ 所有实例共享
console.log(`Hello, I'm ${this.name}`);
};
3. 原型链(Prototype Chain)
什么是原型链?
当访问一个对象的属性时,JavaScript 会:
- 先在对象自身查找
- 如果找不到,就去它的原型上找
- 如果还找不到,就去原型的原型上找
- 直到找到
null
为止
这种一层层的查找关系就形成了"原型链"。
原型链示例
javascript
const grandparent = { grandpa: '老爷爷' };
const parent = { papa: '爸爸' };
const child = { child: '孩子' };
// 设置原型链:child -> parent -> grandparent
Object.setPrototypeOf(parent, grandparent);
Object.setPrototypeOf(child, parent);
console.log(child.child); // "孩子" - 自身属性
console.log(child.papa); // "爸爸" - 父级原型属性
console.log(child.grandpa); // "老爷爷" - 祖父级原型属性
console.log(child.xxx); // undefined - 找不到
4. 完整的原型链图示
javascript
[你的对象] → Object.prototype → null
↑
[数组对象] → Array.prototype → Object.prototype → null
↑
[函数对象] → Function.prototype → Object.prototype → null
实际代码验证
javascript
const arr = [1, 2, 3];
// 数组的原型链
console.log(arr.__proto__ === Array.prototype); // true
console.log(Array.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
// 函数的原型链
function test() {}
console.log(test.__proto__ === Function.prototype); // true
console.log(Function.prototype.__proto__ === Object.prototype); // true
5. 重要的内置原型
Object.prototype
javascript
// 所有对象的最终原型
console.log(Object.prototype.toString()); // "[object Object]"
console.log(Object.prototype.hasOwnProperty); // 检查自身属性的方法
Array.prototype
javascript
const arr = [1, 2, 3];
console.log(Array.prototype.push === arr.push); // true
console.log(Array.prototype.map === arr.map); // true
Function.prototype
javascript
function test() {}
console.log(Function.prototype.call === test.call); // true
console.log(Function.prototype.bind === test.bind); // true
6. 实际应用场景
1. 继承的实现
javascript
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};
function Dog(name, breed) {
Animal.call(this, name); // 调用父类构造函数
this.breed = breed;
}
// 设置原型链继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('Woof!');
};
const myDog = new Dog('Buddy', 'Golden');
myDog.eat(); // "Buddy is eating" (继承的方法)
myDog.bark(); // "Woof!" (自身的方法)
2. 方法扩展(猴子补丁)
javascript
// 为数组添加自定义方法
Array.prototype.last = function() {
return this[this.length - 1];
};
const arr = [1, 2, 3, 4];
console.log(arr.last()); // 4
3. 属性检查
javascript
const obj = { a: 1 };
console.log(obj.hasOwnProperty('a')); // true - 自身属性
console.log(obj.hasOwnProperty('toString')); // false - 继承属性
console.log('toString' in obj); // true - 包括继承属性
7. 注意事项
1. 性能考虑
javascript
// 原型链太长会影响查找性能
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // 只遍历自身属性
console.log(key);
}
}
2. 现代替代方案
javascript
// ES6 class 语法(本质还是基于原型)
class Person {
constructor(name) {
this.name = name;
}
sayHello() { // 这个方法会在 Person.prototype 上
console.log(`Hello, ${this.name}`);
}
}
// 等同于
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, ${this.name}`);
};
总结
概念 | 说明 | 示例 |
---|---|---|
原型 | 每个对象都有一个指向原型的链接 | obj.__proto__ |
原型链 | 通过原型链接形成的查找链条 | obj → proto1 → proto2 → null |
作用 | 实现继承和方法共享 | 所有数组共享 Array.prototype 的方法 |
重点 | 属性查找会沿着原型链向上 | obj.toString() 找到 Object.prototype.toString |
原型机制是 JavaScript 实现面向对象编程的基础,理解它对于掌握 JavaScript 至关重要!