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. 构造函数继承

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

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 面向对象编程。

相关推荐
海上彼尚2 小时前
Nodejs也能写Agent - 17.LangGraph篇 - Checkpointer 与 Memory
前端·javascript·人工智能·langchain·node.js
早期的虫儿有鸟吃2 小时前
Vue3
前端·javascript·vue.js
Highcharts.js2 小时前
商业授权与开源图表库如何选择?
前端·javascript·开源·highcharts·chart.js·echarts.js
一缕清烟在人间2 小时前
鸿蒙应用开发实战【27】— DatabaseService 数据库初始化
javascript·数据库·harmonyos·鸿蒙系统
研☆香3 小时前
JavaScript 绘制简单不规则图形:三角形与五角星实战教程
开发语言·javascript·ecmascript
想要成为糕糕手3 小时前
NO.48 旋转图像 —— LeetCode 热题 100 面试导向深度解析
javascript·算法·面试
铁皮饭盒3 小时前
Bun 制作 IDE 的第一个坑:Electrobun 模拟缩放和关闭,没想到这么难
javascript
JieE2123 小时前
LeetCode 138 随机链表的复制|两种解法详解(哈希表 + 原地 O (1) 空间)
javascript·算法·面试
用户852495071843 小时前
RAG实战:从零打造智能知识库问答系统
javascript