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

相关推荐
烬羽7 小时前
React Router v7 完全指南:一个 Demo 吃透前端路由
javascript·react.js·前端框架
烬羽7 小时前
从"整个页面刷新"到"丝滑切换":手写一个 HashRouter 彻底搞懂前端路由
javascript·前端框架·全栈
用户938515635078 小时前
从零理解 React Router v6:每一个 API 都是怎么工作的
前端·javascript·全栈
kyriewen10 小时前
别再这样写条件渲染了——你的React组件里藏着这5种定时炸弹
前端·javascript·react.js
梦想CAD控件11 小时前
网页端CAD的图形选择、编辑与夹点操作教程
前端·javascript·node.js
月月大王的3D日记12 小时前
Three.js 入门系列(8):六种光源全解析 —— 关灯了,开光!
前端·javascript
自然 醒12 小时前
v-tooltip自定义指令封装
前端·javascript·vue.js
其美杰布-富贵-李14 小时前
第 10 篇:灯光与阴影
javascript·three.js
小高00716 小时前
🔥🔥🔥TypeScript 7 正式版来了:别只看 10 倍速度,这 4 个迁移坑更值得注意
前端·javascript·面试
di24k24k17 小时前
多个 el-form 共用同一 ref 导致表单校验部分失效
前端·javascript·vue.js·elementui