当前位置:首页 > 技术 > 正文内容

JavaScript 继承模式详解与代码示例

访客 技术 2026年9月25日 15

JavaScript 提供了多种实现继承的方式,每种方式都有其优缺点。以下将详细介绍几种常见的继承模式,并提供相应的代码示例。

1. 构造函数继承(借用构造函数)

此方法通过在子类构造函数中调用父类构造函数来继承父类的属性。它能够继承父类的实例属性,但无法继承父类原型上的方法。


function Animal(name) {
    this.name = name;
    this.species = "哺乳动物";
}

Animal.prototype.move = function() {
    console.log("正在移动...");
};

function Dog(breed, name) {
    Animal.call(this, name); // 调用 Animal 构造函数,继承实例属性
    this.breed = breed;
}

Dog.prototype.bark = function() {
    console.log("汪汪叫!");
};

var dog1 = new Dog("金毛", "旺财");
console.log(dog1);
// 输出: Dog { name: '旺财', species: '哺乳动物', breed: '金毛' }
// dog1.move() 会报错,因为 move 是 Animal.prototype 上的方法
    

2. 原型继承

这种方式将子类的原型指向父类的实例。它能够继承父类原型上的方法,但存在一个问题:子类的所有实例会共享同一个父类实例,导致原型污染。


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

Person.prototype.sayHello = function() {
    console.log("你好,我是 " + this.name);
};

function Student(id) {
    this.id = id;
}

Student.prototype = new Person(); // 将 Student 的原型指向 Person 的实例
Student.prototype.study = function() {
    console.log(this.name + " 正在学习。");
};

var student1 = new Student("S001");
student1.name = "小明"; // 给 student1 实例设置 name 属性
student1.sayHello();    // 输出: 你好,我是 小明
student1.study();       // 输出: 小明 正在学习。

var student2 = new Student("S002");
// student2.name 此时也是 undefined,因为 Person 实例的 name 属性被 student1 占用
// student2.sayHello(); // 输出: 你好,我是 undefined

// 错误示例:直接将原型指向父类原型,会导致 constructor 指向错误
// Student.prototype = Person.prototype;
    

3. 组合继承(原型链 + 构造函数)

这是最常用且推荐的继承方式之一。它结合了构造函数继承和原型继承的优点:既能继承父类的实例属性,又能继承父类原型上的方法,并且子类实例之间互不影响。


function Vehicle(brand) {
    this.brand = brand;
    this.wheels = 4;
}

Vehicle.prototype.start = function() {
    console.log(this.brand + " 正在启动...");
};

function Car(model, brand) {
    Vehicle.call(this, brand); // 继承 Vehicle 的实例属性
    this.model = model;
}

// 继承 Vehicle 的原型方法,并重置 constructor
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;

Car.prototype.drive = function() {
    console.log(this.brand + " 的 " + this.model + " 正在行驶...");
};

var myCar = new Car("Tesla Model 3", "Tesla");
console.log(myCar);
// 输出: Car { brand: 'Tesla', wheels: 4, model: 'Tesla Model 3' }
myCar.start(); // 输出: Tesla 正在启动...
myCar.drive(); // 输出: Tesla 的 Tesla Model 3 正在行驶...
    

4. 寄生组合继承

这是组合继承的优化版本,它避免了在子类原型上创建不必要的父类实例。通过创建一个中间函数,将其原型指向父类原型,然后让子类原型指向该中间函数的实例。


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

Electronics.prototype.powerOn = function() {
    console.log(this.name + " 已开机。");
};

function Smartphone(model, name) {
    Electronics.call(this, name); // 继承实例属性
    this.model = model;
}

// 寄生组合继承
function inheritPrototype(subType, superType) {
    var prototype = Object.create(superType.prototype); // 创建父类原型的新实例
    prototype.constructor = subType;                   // 修正 constructor 指向
    subType.prototype = prototype;                     // 修正子类原型
}

inheritPrototype(Smartphone, Electronics);

Smartphone.prototype.call = function() {
    console.log(this.model + " 正在拨打电话...");
};

var myPhone = new Smartphone("iPhone 14", "Apple");
console.log(myPhone);
// 输出: Smartphone { name: 'Apple', model: 'iPhone 14' }
myPhone.powerOn(); // 输出: Apple 已开机。
myPhone.call();    // 输出: iPhone 14 正在拨打电话...
    

5. ES6 Класс 继承

ES6 引入了 `class` 关键字,提供了更简洁、更面向对象的语法糖来实现继承。`extends` 关键字用于继承,`super` 关键字用于调用父类的方法(包括构造函数)。


class Animal {
    constructor(name) {
        this.name = name;
        this.type = "动物";
    }

    eat() {
        console.log(this.name + " 正在进食。");
    }
}

class Mammal extends Animal {
    constructor(name, species) {
        super(name); // 调用父类构造函数
        this.species = species;
    }

    sleep() {
        console.log(this.name + " 正在睡觉。");
    }
}

var lion = new Mammal("狮子王", "猫科");
console.log(lion);
// 输出: Mammal { name: '狮子王', type: '动物', species: '猫科' }
lion.eat();  // 输出: 狮子王 正在进食。
lion.sleep(); // 输出: 狮子王 正在睡觉。
    

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。