在 JavaScript 的世界里,原型(Prototype)、原型链(Prototype Chain) 和 继承(Inheritance) 是核心且容易混淆的概念。它们共同构成了 JavaScript 面向对象编程(OOP)的基础,也是面试中高频考察的知识点。本文将从底层原理出发,层层递进,帮助你彻底掌握这三者的关系与运作机制。
与其他面向对象语言(如 Java、C++)不同,JavaScript 并没有“类”的概念(ES6 的 class 也只是语法糖),而是基于 对象 构建一切。为了实现对象之间的属性和方法共享,JavaScript 引入了 原型机制。
简单来说:原型是对象之间共享属性和行为的一种机制。
每个 JavaScript 函数(除了箭头函数)在创建时都会自动拥有一个名为 prototype 的属性,它指向一个对象——这就是 原型对象。
function Person(name) {
this.name = name;
}
console.log(Person.prototype); // { constructor: Person }同时,每个对象(包括实例对象)内部都有一个隐式属性 [[Prototype]](在大多数浏览器中可通过 __proto__ 访问),它指向其构造函数的 prototype 对象。
const p1 = new Person('Alice');
console.log(p1.__proto__ === Person.prototype); // true✅ 关键点:
prototype 是函数的属性。__proto__(或 [[Prototype]])是对象的内部属性。__proto__ 链接到构造函数的 prototype。当访问一个对象的属性时,JavaScript 引擎会按以下顺序查找:
__proto__ 向上查找其原型对象;Object.prototype;undefined。这个逐级向上查找的链条,就是 原型链。
function Animal() {}
Animal.prototype.eat = function() { console.log('eating...'); };
function Dog() {}
Dog.prototype = new Animal(); // 继承 Animal
Dog.prototype.bark = function() { console.log('barking!'); };
const dog = new Dog();
dog.bark(); // barking!
dog.eat(); // eating! (从 Animal.prototype 继承而来)
// 原型链示意:
// dog → Dog.prototype → Animal.prototype → Object.prototype → null🌟 所有原型链的终点都是
Object.prototype,而Object.prototype.__proto__为null,表示链的终结。
由于 JavaScript 没有传统意义上的“类继承”,它通过 原型链 来模拟继承行为。以下是几种常见的继承方式:
function Parent() { this.name = 'parent'; }
Parent.prototype.say = function() { return this.name; };
function Child() {}
Child.prototype = new Parent(); // 关键:让 Child.prototype 指向 Parent 实例
const c = new Child();
console.log(c.say()); // 'parent'缺点:所有子实例共享父类引用类型属性,且无法向父构造函数传参。
function Parent(name) { this.name = name; }
function Child(name) {
Parent.call(this, name); // 借用父构造函数
}优点:可传参,避免引用共享。 缺点:无法继承父类原型上的方法。
结合原型链 + 借用构造函数:
function Parent(name) {
this.name = name;
}
Parent.prototype.say = function() { return this.name; };
function Child(name, age) {
Parent.call(this, name); // 借用构造函数,传参 + 避免共享
this.age = age;
}
Child.prototype = new Parent(); // 原型链继承方法
Child.prototype.constructor = Child; // 修正 constructor
const c = new Child('Tom', 10);
console.log(c.say(), c.age); // 'Tom', 10class 与 extendsES6 引入了 class 语法糖,底层仍是基于原型链:
class Parent {
constructor(name) { this.name = name; }
say() { return this.name; }
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类构造函数
this.age = age;
}
}class 写法更清晰,但理解其背后的原型机制依然至关重要。
误区 | 正确理解 |
|---|---|
“prototype 是所有对象都有的属性” | ❌ 只有函数才有 prototype;对象有 __proto__ |
“修改 __proto__ 会影响构造函数的 prototype” | ❌ 它们是引用关系,但直接修改 __proto__ 不推荐(性能差) |
“instanceof 判断的是构造函数” | ❌ 实际是检查对象的原型链中是否包含构造函数的 prototype |
[] instanceof Array; // true
// 等价于:Array.prototype 是否在 [] 的原型链上?prototype 属性,用于共享方法。__proto__ 逐级向上查找属性的机制。Object.create()、class extends)实现。理解这三者的关系,就掌握了 JavaScript 面向对象的“灵魂”。无论你使用的是传统函数构造器,还是现代 class 语法,其底层逻辑始终围绕 原型链 展开。
💡 记住一句话: “在 JavaScript 中,万物皆对象,对象皆有原型,原型构成链条,链条实现继承。”
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。