原型与原型链
概念描述原型 (prototype)对象的隐式父对象,用于属性继承和方法共享。原型链 (prototype chain)对象通过原型层层向上查找属性的链式结构,最终以null结束。构造函数 & 原型通过new创建对象,实例的__proto__指向构造函数的prototype。ES6 类底层通过原型实现继承,语法更清晰。好的,我们来系统讲解JavaScript 的继承,从基础概念到实现方式,再结合原
一、原型(Prototype)
1. 概念
在 JavaScript 中,几乎所有对象都有一个 内部属性 [[Prototype]](在大部分浏览器中可以通过 __proto__ 访问),它指向另一个对象,这个对象就是 原型。
原型的作用主要有两个:
- 属性继承:对象可以访问原型上的属性和方法。
- 方法共享:多个对象可以共享同一个方法,而不必在每个对象中重复定义。
2. 构造函数与原型
在 JavaScript 中,构造函数创建对象时,会自动把对象的原型指向构造函数的 prototype:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const alice = new Person('Alice');
alice.sayHello(); // Hello, I'm Alice
// 查看原型
console.log(alice.__proto__ === Person.prototype); // true
✅ 这里有几个关键点:
Person.prototype是原型对象。alice.__proto__指向Person.prototype。- 原型上定义的方法
sayHello被实例alice共享,而不是每个实例都复制一份。
3. 内置对象的原型
JavaScript 中的数组、函数等内置对象也有原型:
const arr = [1, 2, 3];
console.log(arr.__proto__ === Array.prototype); // true
console.log(arr.__proto__.__proto__ === Object.prototype); // true
这里我们就看到 原型链 的雏形。
二、原型链(Prototype Chain)
1. 概念
原型链指的是对象通过 __proto__ 一层层向上查找属性的链式结构,直到找到 null(Object.prototype.__proto__ === null)。
当访问对象的属性或方法时,JS 引擎的查找顺序是:
- 先在对象自身属性中查找。
- 如果找不到,则去对象的原型中查找。
- 如果原型还有原型,则继续向上查找。
- 直到原型为
null,仍找不到则返回undefined。
2. 示例
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const bob = new Person('Bob');
bob.sayHello(); // Hello, I'm Bob
// 原型链结构
console.log(bob.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null
访问 bob.sayHello() 时:
- bob 对象自身没有
sayHello属性。 - JS 去
bob.__proto__(即Person.prototype)查找,找到了方法。 - 执行方法。
如果你访问 bob.toString():
- bob 自身没有
toString。 Person.prototype也没有。- JS 去
Person.prototype.__proto__(即Object.prototype)找,找到了toString方法。
✅ 这就是原型链的查找过程。
3. 注意事项
- 原型链是动态的:
Person.prototype.sayAge = function() { console.log('Age unknown'); };
bob.sayAge(); // Age unknown
bob已经存在,但仍可以访问新加的原型方法。
- 避免原型污染:
- 不要在原型上直接放可变的引用类型(如数组、对象),会被所有实例共享。
function Person() {}
Person.prototype.hobbies = [];
const p1 = new Person();
const p2 = new Person();
p1.hobbies.push('reading');
console.log(p2.hobbies); // ['reading'],被共享了
三、原型与类的关系
ES6 的 class 本质上是语法糖,底层仍然用原型实现:
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log(`Hello, I'm ${this.name}`);
}
}
const cindy = new Person('Cindy');
console.log(cindy.__proto__ === Person.prototype); // true
所以理解原型链对理解 ES6 类的继承也非常重要。
四、总结
| 概念 | 描述 |
|---|---|
| 原型 (prototype) | 对象的隐式父对象,用于属性继承和方法共享。 |
| 原型链 (prototype chain) | 对象通过原型层层向上查找属性的链式结构,最终以 null 结束。 |
| 构造函数 & 原型 | 通过 new 创建对象,实例的 __proto__ 指向构造函数的 prototype。 |
| ES6 类 | 底层通过原型实现继承,语法更清晰。 |
好的,我们来系统讲解 JavaScript 的继承,从基础概念到实现方式,再结合原型链讲解。
五、继承的概念
继承指的是一个对象或类 获取另一个对象或类的属性和方法 的能力。
在 JavaScript 中,继承主要通过 原型链 实现,也可以通过 ES6 class 或 对象组合实现。
六、原型链继承(最基础的继承方式)
1️⃣ 例子
function Person(name) {
this.name = name;
this.skills = ['JS', 'HTML'];
}
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
function Student(name, grade) {
this.grade = grade;
Person.call(this, name); // 调用父构造函数,继承实例属性
}
// 继承方法(原型链继承)
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.study = function() {
console.log(`${this.name} is studying in grade ${this.grade}`);
};
const stu1 = new Student('张三', 10);
stu1.sayHello(); // Hello, I'm 张三
stu1.study(); // 张三 is studying in grade 10
2️⃣ 原理
Student.prototype = Object.create(Person.prototype)- 创建一个新对象,新对象的原型指向
Person.prototype - 让
Student的实例可以访问Person的方法(继承方法)
- 创建一个新对象,新对象的原型指向
Person.call(this, name)- 调用父构造函数,继承实例属性(每个实例有独立副本)
✅ 这样做就解决了 原型链继承共享引用属性的问题(如数组、对象会被共享)。
七、ES6 class 继承
ES6 提供了更简洁的继承语法:
class Person {
constructor(name) {
this.name = name;
this.skills = ['JS', 'HTML'];
}
sayHello() {
console.log(`Hello, I'm ${this.name}`);
}
}
class Student extends Person {
constructor(name, grade) {
super(name); // 调用父类构造函数
this.grade = grade;
}
study() {
console.log(`${this.name} is studying in grade ${this.grade}`);
}
}
const stu2 = new Student('李四', 11);
stu2.sayHello(); // Hello, I'm 李四
stu2.study(); // 李四 is studying in grade 11
extends实现原型链继承super()调用父类构造函数,继承实例属性
✅ ES6 的继承底层依然是 原型链 + 构造函数调用,只是语法更清晰。
八、对象继承(Object.create)
有时不想用构造函数,可以直接用对象继承:
const person = {
name: '张三',
sayHello() {
console.log(`Hello, I'm ${this.name}`);
}
};
const student = Object.create(person);
student.grade = 10;
student.study = function() {
console.log(`${this.name} is studying in grade ${this.grade}`);
};
student.sayHello(); // Hello, I'm 张三
student.study(); // 张三 is studying in grade 10
✅ Object.create(person) 创建了一个对象,原型指向 person,实现了 原型继承。
九、继承方式总结
| 方式 | 实现特点 | 注意点 |
|---|---|---|
| 原型链继承 | 子类原型指向父类实例或父类原型,方法继承共享 | 实例属性共享引用类型,需配合构造函数继承解决 |
| 构造函数继承(借用构造函数) | 在子构造函数中调用父构造函数,继承实例属性 | 方法无法继承,需要原型链补充方法 |
| 组合继承(最常用) | 原型链继承 + 构造函数继承,兼顾方法和属性 | 经典方案 |
| ES6 class 继承 | extends + super(),语法糖封装组合继承 |
更直观,底层仍是原型链 + 构造函数 |
| 对象继承(Object.create) | 创建对象,原型指向另一个对象 | 简单对象继承,适合轻量对象 |
十、 JavaScript 继承中实例属性、原型方法和原型链层级 的关系。
下面是一个示例,以 Person 和 Student 为例:
| 层级 | 对象/原型 | 属性/方法示例 | 说明 |
|---|---|---|---|
| 实例 | stu (Student 实例) |
name, grade |
调用 super() 继承父类实例属性,子类自己的属性也在这里 |
| 子类原型 | Student.prototype |
study() |
子类特有方法定义在这里,实例通过原型链访问 |
| 父类原型 | Person.prototype |
sayHello() |
父类方法,实例通过原型链继承访问 |
| Object 原型 | Object.prototype |
toString(), hasOwnProperty() |
所有对象共享的内置方法 |
| 原型链终点 | null |
- | 原型链结束,没有父对象 |
解释
- 实例属性
- 通过
super()调用父类构造函数继承,或子类自己定义。 - 每个实例都有自己的独立副本。
- 通过
- 子类原型方法
- 定义在
Student.prototype上。 - 所有 Student 实例共享。
- 定义在
- 父类原型方法
- 定义在
Person.prototype上。 - 通过原型链访问,实现方法继承。
- 定义在
- Object.prototype 方法
- 所有对象最终都可以访问。
更多推荐


所有评论(0)