学习笔记-JavaScript的原型和原型链

81 阅读1分钟

一、原型(Prototype)基础概念

1.1 什么是原型

在JavaScript中,每个函数都拥有一个特殊的prototype属性(称为显式原型),该属性指向一个特定的对象——即原型对象。当使用new操作符创建实例时,这个实例会自动继承其构造函数prototype对象上定义的所有属性和方法。


function Person(name) {     
  this.name = name; 
}  
// 向Person函数的prototype添加方法
Person.prototype.sayHello = function() {
  console.log(`Hello, I'm ${this.name}`); 
};  
const person1 = new Person('Alice');
person1.sayHello(); // 输出:"Hello, I'm Alice" 

1.2 实例的隐式原型 proto

除了null以外,每个JavaScript对象都包含一个内部属性[[Prototype]](在浏览器环境中可通过__proto__访问),这就是所谓的隐式原型。实例的__proto__直接指向其构造函数的prototype对象。

console.log(person1.__proto__ === Person.prototype); // 输出:true

核心关系:实例.proto === 构造函数.prototype

二、原型链(Prototype Chain)机制

2.1 原型链的形成

当访问对象的某个属性或方法时,JavaScript引擎遵循以下查找顺序:
首先在对象自身的属性中搜索
若未找到,则通过__proto__进入其原型对象继续查找
若原型对象中仍未找到,则继续沿__proto__向上追溯,直至遇到null

这种通过原型对象层层连接的查找路径构成了原型链。


function Animal(type) {     
    this.type = type; 
} 
Animal.prototype.breathe = function() {  
    console.log('Breathing...'); 
};  
function Dog(name) {    
    this.name = name; 
}  
// 构建原型链:Dog.prototype → Animal.prototype 
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {  
    console.log('Woof!'); 
};  
const myDog = new Dog('Buddy');  // 原型链查找过程演示: 
myDog.bark();     // 1. 在自身找到 → 立即执行
myDog.breathe();  // 2. 自身无 → Dog.prototype无 → 在Animal.prototype中找到 → 执行 
myDog.toString(); // 3. 沿原型链追溯至Object.prototype → 执行 

2.2 完整的原型链结构

myDog → proto (指向Dog.prototype) → proto (指向Animal.prototype) → proto (指向Object.prototype) → proto (指向null)

三、关键特性与内置对象

3.1 构造函数与原型的关系


function Car(brand) {     
    this.brand = brand; 
}  
console.log(Car.prototype.constructor === Car); // 输出:true  
const myCar = new Car('Toyota'); 
console.log(myCar.constructor === Car); // 输出:true 

3.2 内置构造函数的原型链


const arr = [1, 2, 3];  // 原型链:arr → Array.prototype → Object.prototype → null
const str = 'hello'; // 原型链:str → String.prototype → Object.prototype → null 
const num = 123; // 原型链:num → Number.prototype → Object.prototype → null 

四、实际应用场景

4.1 方法共享与内存优化


// 不推荐:每个实例都会创建独立的say方法
function BadPerson(name) {    
    this.name = name;    
    this.say = function() {
        console.log(this.name); 
    }; 
}  
// 推荐:将方法定义于原型上,实现所有实例共享 
function GoodPerson(name) {   
    this.name = name; 
} 
GoodPerson.prototype.say = function() { 
    console.log(this.name);
}; 

4.2 继承实现


// 父类定义 
function Shape(color) {
    this.color = color; 
}  
Shape.prototype.getArea = function() { 
    throw new Error('必须在子类中实现此方法'); 
};  
// 子类定义 
function Circle(radius, color) {  
    Shape.call(this, color); // 调用父类构造函数     
    this.radius = radius;
}  
// 实现原型继承 
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle; 
// 子类特有方法 
Circle.prototype.getArea = function() { 
    return Math.PI * this.radius * this.radius; 
}; 

五、常用方法与属性

5.1 原型相关方法


function Parent() {}
function Child() {}  
// 设置原型链关系 
Child.prototype = Object.create(Parent.prototype);  
// 验证原型关系 
console.log(Child.prototype.isPrototypeOf(new Child())); // 输出:true
console.log(Parent.prototype.isPrototypeOf(new Child())); // 输出:true  
// 属性检查 
const obj = new Child();
console.log(obj.hasOwnProperty('xxx')); // 检查对象自身属性 
console.log('xxx' in obj); // 检查整个原型链 

5.2 现代JavaScript中的原型

ES6引入的class语法实质上是原型机制的语法糖:


class Animal { 
    constructor(name) {   
        this.name = name;   
    }          
    speak() {  
        console.log(`${this.name} makes a noise.`);     
        } 
    }  
    
// 等效于传统原型写法:
function Animal(name) {  
    this.name = name; 
} 

Animal.prototype.speak = function() { 
    console.log(`${this.name} makes a noise.`);
}; 

六、常见误区与最佳实践

6.1 避免的陷阱


// 错误做法:直接替换整个
prototype function MyClass() {}

const instance1 = new MyClass();

MyClass.prototype = {   
    newMethod: function() {} 
}; // 这会破坏已创建实例的原型链  

// 正确做法:在现有prototype上添加方法 

MyClass.prototype.newMethod = function() {}; 

6.2 性能考量

过长的原型链会增加属性查找时间 对频繁访问的属性,建议直接定义为实例属性 使用Object.create(null)创建无原型对象可提升特定场景下的性能

七、核心总结

所有函数都具备prototype属性
所有对象都包含__proto__属性
原型链的终点始终是null
实例从构造函数的prototype继承属性和方法
ES6 class本质是基于原型体系的语法封装