javascript之对象的继承

JavaScript 对象继承

JavaScript 对象的继承和基于类的继承(如 Java、C++)有本质区别。JavaScript 采用原型继承(Prototypal Inheritance),这是一种基于原型链(Prototype Chain)的继承机制。

1. 原型链继承

这是最基本的继承方式,通过将子类的原型指向父类的实例来实现。

javascript 复制代码
// 父类构造函数
function Animal(name) {
    this.name = name;
}

// 父类方法
Animal.prototype.sayName = function() {
    console.log('My name is ' + this.name);
};

// 子类构造函数
function Dog(name, breed) {
    Animal.call(this, name); // 调用父类构造函数
    this.breed = breed;
}

// 关键步骤:将 Dog.prototype 的原型指向 Animal.prototype
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.sayName(); // My name is Buddy
myDog.bark();    // Woof!
console.log(myDog instanceof Dog);    // true
console.log(myDog instanceof Animal); // true

**优点:**实现简单,能继承父类原型上的属性和方法。

**缺点:**所有子类实例共享同一个父类实例的引用属性,修改一个会影响所有。

2. 构造函数继承

在子类构造函数中调用父类构造函数,使用 call 或 apply 方法。

javascript 复制代码
function Animal(name) {
    this.name = name;
    this.colors = ['black', 'white'];
}

function Dog(name, breed) {
    Animal.call(this, name); // 继承实例属性
    this.breed = breed;
}

const dog1 = new Dog('Max', 'Husky');
dog1.colors.push('brown');

const dog2 = new Dog('Bella', 'Poodle');
console.log(dog2.colors); // ['black', 'white'],不受 dog1 影响

**优点:**解决了引用属性共享的问题,每个实例都有独立的属性副本。

**缺点:**无法继承父类原型上的方法。

3. 组合继承(最常用)

结合原型链继承和构造函数继承,既继承方法又继承属性。

javascript 复制代码
function Animal(name) {
    this.name = name;
    this.colors = ['black', 'white'];
}

Animal.prototype.sayName = function() {
    console.log('My name is ' + this.name);
};

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 dog1 = new Dog('Charlie', 'Beagle');
const dog2 = new Dog('Lucy', 'Dachshund');

dog1.colors.push('brown');
console.log(dog1.colors); // ['black', 'white', 'brown']
console.log(dog2.colors); // ['black', 'white']
dog1.sayName(); // My name is Charlie
dog1.bark();    // Woof!

**优点:**融合了两种继承方式的优点,是 JavaScript 中最常用的继承模式。

**缺点:**父类构造函数被调用了两次。

4. 原型式继承

基于已有对象创建新对象,使用 Object.create()。

javascript 复制代码
const animal = {
    name: 'Animal',
    sayName: function() {
        console.log('My name is ' + this.name);
    }
};

const dog = Object.create(animal, {
    breed: {
        value: 'Golden Retriever',
        writable: true,
        enumerable: true
    },
    bark: {
        value: function() {
            console.log('Woof!');
        },
        writable: true,
        enumerable: true
    }
});

dog.name = 'Buddy';
dog.sayName(); // My name is Buddy
dog.bark();    // Woof!

**适用场景:**不需要创建构造函数,只想让一个对象继承另一个对象。

5. 寄生式继承

在原型式继承的基础上增强对象,添加额外的方法或属性。

javascript 复制代码
function createDog(original) {
    const clone = Object.create(original);
    clone.bark = function() {
        console.log('Woof!');
    };
    return clone;
}

const animal = {
    name: 'Animal',
    sayName: function() {
        console.log('My name is ' + this.name);
    }
};

const dog = createDog(animal);
dog.name = 'Max';
dog.sayName(); // My name is Max
dog.bark();    // Woof!

6. 寄生组合式继承(最优方案)

ES5 时代最理想的继承方式,解决了组合继承中父类构造函数被调用两次的问题。

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

function Animal(name) {
    this.name = name;
    this.colors = ['black', 'white'];
}

Animal.prototype.sayName = function() {
    console.log('My name is ' + this.name);
};

function Dog(name, breed) {
    Animal.call(this, name); // 只调用一次父类构造函数
    this.breed = breed;
}

// 使用寄生组合式继承
inheritPrototype(Dog, Animal);

Dog.prototype.bark = function() {
    console.log('Woof!');
};

const dog = new Dog('Rocky', 'Bulldog');
dog.sayName(); // My name is Rocky
dog.bark();    // Woof!

7. ES6 Class 继承

ES6 引入了 class 语法糖,底层仍然是基于原型的继承。

javascript 复制代码
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    sayName() {
        console.log(`My name is ${this.name}`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name); // 调用父类的 constructor
        this.breed = breed;
    }
    
    bark() {
        console.log('Woof!');
    }
}

const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.sayName(); // My name is Buddy
myDog.bark();    // Woof!

注意: ES6 的 class 只是语法糖,JavaScript 的继承机制仍然是基于原型的。

总结对比

继承方式 优点 缺点 适用场景
原型链继承 简单,能继承原型方法 引用属性共享,无法向父类传参 简单继承场景
构造函数继承 可向父类传参,引用属性独立 无法继承原型方法 需要独立属性副本的场景
组合继承 融合两者优点 父类构造函数调用两次 通用场景(ES5)
寄生组合式继承 最优方案,效率高 实现稍复杂 追求性能的场景
ES6 Class 语法简洁,易读易写 本质仍是原型继承 现代 JavaScript 开发

在实际开发中,推荐使用 ES6 的 class 语法,它更清晰、更易维护。理解底层的原型继承机制有助于深入掌握 JavaScript 面向对象编程。

相关推荐
IT_Octopus20 分钟前
【零基础入门 LLM 开发 · Day 11】:ChatOpenAI vs init_chat_model——一行换供应商
java·前端·javascript
King禅36 分钟前
JavaScript 调试实战指南:从 console.log 到 DevTools 深度运用
javascript
全栈项目管理程序猿41 分钟前
ArcGIS JS 基础教程(28):图层渲染顺序管理
前端·javascript
扶风ff1 小时前
练题簿在线免费刷题:创建个人任务,把刷题、听题和模考目标放到每天的学习中
开发语言·javascript·学习·小程序
asong1 小时前
没有源代码,AI 也能拆解软件:这个开源项目已有 3.7 万 Star
javascript·后端·github
用户1733598075371 小时前
纯前端 PDF 压平避坑指南:压平后表单字段变了?
前端·javascript·vue.js
用户298698530142 小时前
React 中实现 Word 与 Markdown 双向转换的实践记录
javascript·react.js·markdown
OpsEye2 小时前
大模型会话里的手机号、身份证,怎么自动脱敏?
javascript·ai编程
爱喝水的小周2 小时前
实验十 Vue‑Router 王者荣耀战绩查询 APP 开发实践
前端·javascript·vue.js
yexianglunbai2 小时前
Vue 3 全面详解:从核心特性到工程化实践
前端·javascript·vue.js