前端知识速记—JS篇:原型与原型链

发布于:2025-02-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

前端知识速记—JS篇:原型与原型链

在JavaScript中,原型和原型链是非常重要的概念,它们帮助我们理解对象的创建和继承机制。


1. 什么是原型?

在JavaScript中,原型是构造函数所关联的一个对象,它决定了通过该构造函数创建的所有实例所共享的属性和方法。概念可以进一步分解为以下几点:

1.1 构造函数的原型属性

每个构造函数都有一个特殊的prototype属性,它是一个对象,定义了所有通过该构造函数创建的实例所可以共享的属性和方法。

示例:
function Person(name) {
    this.name = name;  // 实例属性
}

// 定义共享方法
Person.prototype.sayHello = function() {
    console.log(`你好,我是${this.name}`);
};

// 创建实例
const alice = new Person('Alice');
alice.sayHello();  // 输出:你好,我是Alice

在这个例子中,Person构造函数的原型对象(Person.prototype)中定义了sayHello方法。所有使用Person构造函数创建的实例都可以访问这个方法。

1.2 实例与原型的关系

当你创建一个新实例时,该实例内部定义了一个指向其构造函数原型的__proto__属性。通过这个属性,实例可以访问原型上定义的方法和属性。

示例:
console.log(alice.__proto__ === Person.prototype);  // 输出:true

这里,alice.__proto__指向了Person.prototype,因此alice可以访问到sayHello方法。

1.3 访问构造函数

每个实例对象都有一个constructor属性,指向它的构造函数。这使得我们可以从实例中反向确认它的创建来源。

示例:
console.log(alice.constructor === Person);  // 输出:true

这告诉我们,alice是通过Person构造函数创建的。


2. 什么是原型链?

原型链是JavaScript中实现对象继承的关键机制。它是由多个原型构成的链条,每个对象都有一个__proto__属性,指向其构造函数的原型对象。以下是关于原型链的详细说明:

2.1 原型链的结构

每个对象都有一个__proto__属性,这个属性指向其构造函数的原型。原型对象同样可以有自己的原型,如此层层递归,直到找到顶层原型Object.prototype,或者为null为止,形成一个原型链。

示例:
console.log(alice.__proto__ === Person.prototype);  // 输出:true
console.log(Person.prototype.__proto__ === Object.prototype);  // 输出:true
console.log(Object.prototype.__proto__);  // 输出:null

2.2 属性查找

当你访问一个对象的属性或方法时,JavaScript会首先检查该对象自身(即实例)是否有这个属性。如果没有,JavaScript将沿着原型链向上查找,直到找到该属性或者到达链的顶端。

示例:
console.log(alice.name); // 输出:Alice (直接在实例上找到)
console.log(alice.sayHello); // 输出:function (通过原型找到)
console.log(alice.age); // 输出:undefined (在实例和原型上都找不到)

总之,如果在实例对象中找不到某个属性或方法,JavaScript会沿着__proto__属性链向上搜索,直到找到为止。

2.3 顶级原型

原型链的顶层原型是Object.prototype,它包含了所有JavaScript对象的基本方法和属性。如果在原型链的顶端也没有找到所请求的属性或方法,返回undefined

示例:
console.log(Object.prototype);  // 输出:Object {}
console.log(alice.toString);     // 输出:查看在原型链上获取到的toString方法

3. 原型链的继承示例

通过原型链,我们可以让一个构造函数的实例继承另一个构造函数的属性和方法。

3.1 示例分析

function Animal() {}

// 在Animal的原型上定义一个方法
Animal.prototype.eat = function() {
    console.log('正在吃...');
};

function Dog(name) {
    this.name = name;
}

// Dog的原型继承自Animal的实例
Dog.prototype = Object.create(Animal.prototype);

// 在Dog的原型上定义一个方法
Dog.prototype.bark = function() {
    console.log(`${this.name}在叫!`);
};

// 创建Dog的实例
const dog = new Dog('小狗');
dog.eat();  // 输出:正在吃...
dog.bark(); // 输出:小狗在叫!

在这个例子中,Dog的原型通过Object.create(Animal.prototype)指向Animal的原型。这意味着Dog的实例不仅能调用bark方法(它自己定义的方法),还可以调用eat方法(从Animal继承的方法)。