《从分遗产说起:JS 原型与继承详解》

"天天开心就好"


先来讲讲概念:

原型(Prototype)

什么是原型?

原型是 JavaScript 中实现对象间共享属性和方法的机制。每个 JavaScript 对象(除了 null)都有一个内部链接指向另一个对象,这个对象就是它的"原型"(prototype)。

继承(Inheritance)

什么是继承?

继承是面向对象编程中的一个核心概念,它允许一个对象(子对象)获取另一个对象(父对象)的属性和方法。在 JavaScript 中,继承主要通过原型链来实现。

好的现在我们明确了什么是原型,什么是继承。简单来说,原型就是一个机制,每个对象内部都一个内部链接指向他的原型。继承我的理解就是一种行为,就是像继承财产那样继承父对象的属性和方法,可谓是形容十分贴切。

原型基础

原型对象 (prototype)

  • 每个函数都有一个 prototype 属性(箭头函数除外)
  • 这个属性指向一个对象,称为"原型对象"
  • 原型对象包含可以被特定类型的所有实例共享的属性和方法
javascript 复制代码
function Person() {}
Person.prototype.name = 'Default';
Person.prototype.sayHello = function() {
    console.log(`Hello, I'm ${this.name}`);
};

__proto__ 属性

  • 每个对象都有一个 __proto__ 属性(现已标准化为 Object.getPrototypeOf()
  • 指向创建该对象的构造函数的原型对象
javascript 复制代码
const person = new Person();
console.log(person.__proto__ === Person.prototype); // true

这个很好理解了,我在这里想用c语言里面的指针来形容了。prototype就像是地址对应的数据,而_proto_就像是指向他的指针。不太恰当哈

我们经常这样说:对象的继承是通过原型链实现的。

那么什么是原型链:

什么是原型链?

原型链(Prototype Chain)是 JavaScript 中实现继承的核心机制。当访问一个对象的属性或方法时,JavaScript 引擎会沿着对象的原型链向上查找,直到找到该属性或到达原型链的末端(null)。

原型链的构成

  1. ​每个对象都有一个 __proto__ 属性​ (现已标准化为 Object.getPrototypeOf()
  2. ​每个函数都有一个 prototype 属性​
  3. ​原型链的终点是 null

原型链的工作原理

当访问一个对象的属性时:

  1. 首先在对象自身查找该属性
  2. 如果没有找到,则查找对象的 __proto__(即其构造函数的 prototype
  3. 如果还没有找到,继续查找 __proto__.__proto__,依此类推
  4. 直到找到该属性或到达 null(此时返回 undefined
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 Retriever');

// 原型链:
// myDog -> Dog.prototype -> Animal.prototype -> Object.prototype -> null

我们讲原型和继承就是在意js中继承这种行为是怎么实现的,就像现实中大家只在乎怎么分遗产一样!

继承实现方式

1.原型链继承

javascript 复制代码
function Parent() {
    this.parentProperty = true;
}
Parent.prototype.getParentValue = function() {
    return this.parentProperty;
};

function Child() {
    this.childProperty = false;
}
// 继承 Parent
Child.prototype = new Parent();

const instance = new Child();
console.log(instance.getParentValue()); // true

​问题​​:

  • 所有子类实例共享同一个父类实例
  • 无法向父类构造函数传参

2. 构造函数继承

javascript 复制代码
function Parent(name) {
    this.name = name;
}

function Child(name) {
    Parent.call(this, name); // 在子类构造函数中调用父类构造函数
}

const child = new Child('Alice');
console.log(child.name); // 'Alice'

​优点​​:

  • 可以向父类传参
  • 每个子类实例都有独立的父类属性副本

​缺点​​:

  • 无法继承父类原型上的方法

3.组合继承(最常用)

javascript 复制代码
function Parent(name) {
    this.name = name;
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name); // 第二次调用 Parent
    this.age = age;
}
Child.prototype = new Parent(); // 第一次调用 Parent
Child.prototype.constructor = Child; // 修复构造函数指向

const child = new Child('Alice', 25);
child.sayName(); // 'Alice'

​优点​​:

  • 结合了原型链和构造函数的优点
  • 既能继承原型方法,又能保证实例属性独立

​缺点​​:

  • 父类构造函数被调用了两次

4. 原型式继承

javascript 复制代码
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

const parent = { name: 'Parent' };
const child = object(parent);
console.log(child.name); // 'Parent'

ES5 标准化为 Object.create()

javascript 复制代码
const child = Object.create(parent);

5. 寄生式继承

javascript 复制代码
function createAnother(original) {
    const clone = Object.create(original);
    clone.sayHi = function() {
        console.log('Hi');
    };
    return clone;
}

6. 寄生组合式继承(最佳实践)

javascript 复制代码
function inheritPrototype(child, parent) {
    const prototype = Object.create(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

function Parent(name) {
    this.name = name;
}
Parent.prototype.sayName = function() {
    console.log(this.name);
};

function Child(name, age) {
    Parent.call(this, name);
    this.age = age;
}

inheritPrototype(Child, Parent);

const child = new Child('Alice', 25);
child.sayName(); // 'Alice'

​优点​​:

  • 只调用一次父类构造函数
  • 原型链保持正确
  • 最理想的继承方式

ES6 的 class 继承

javascript 复制代码
class Parent {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        console.log(this.name);
    }
}

class Child extends Parent {
    constructor(name, age) {
        super(name); // 调用父类构造函数
        this.age = age;
    }
}

const child = new Child('Alice', 25);
child.sayName(); // 'Alice'

​注意​​:

  • class 本质上是语法糖,底层仍然是基于原型的继承
  • extends 实现了寄生组合式继承
  • 必须在使用 this 前调用 super()

继承是js中很核心的机制了,有很多中方式来实现继承,继承的好处就是我们可以直接继承父对象的方法和属性而不用自己再次定义了。用好继承可以大大提升我们的代码水平,帮助我们实现更多复杂需求。


相关推荐
前端大卫22 分钟前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘38 分钟前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare39 分钟前
浅浅看一下设计模式
前端
Lee川43 分钟前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix1 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人1 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼2 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端