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

相关推荐
zhifou1234567 分钟前
Vue基础(二)
前端·javascript·vue.js
默_笙25 分钟前
🙃 我让爬虫终于看到了我的网站,后端同事说"这也行?"(下):App Router 全栈实战
前端·javascript
九九落31 分钟前
JavaScript 实现北京时间精确到毫秒显示:UTC+8、Asia/Shanghai 与在线时间校准详解
开发语言·javascript·ecmascript
FEF前端团队1 小时前
小程序微信支付 V3 接入实战手册:从商户配置到前后端落地
javascript·后端·node.js
悟空瞎说3 小时前
# Dispatch(GCD)苹果官方文档 白话文讲解(Objective‑C 版本文档)
javascript
七牛开发者3 小时前
Coding Agent 如何跑稳长任务?从上下文管理到运行时状态
前端·javascript·后端
七牛开发者3 小时前
拆解 DeepSeek Harness:Profile 与 Bundle 如何装配运行时
前端·javascript·后端
九九落5 小时前
北京时间校准怎么做?电脑和手机时间不准的排查方法
javascript
念何架构之路5 小时前
Gin响应渲染
前端·javascript·gin
WebInfra6 小时前
Rspack 2.2 发布:30+ 项性能优化,拥抱 Solid 2.0
前端·javascript·前端框架