本文解析JavaScript面向对象编程的核心机制,指出其不同于Java或C#的类概念,而是基于原型(prototype)实现。通过具体示例说明如何创建实例、修改原型指向以及利用Object.create()安全构建对象,帮助开发者掌握JS中继承与多态的真实运作方式。
JavaScript面向对象编程基础
在JavaScript中,所有数据本质上都是对象。这是否意味着我们已经在进行面向对象编程?
答案是否定的。如果仅仅使用Number、Array、String以及通过{...}语法定义的基础对象,并不能真正发挥面向对象编程的优势。
JavaScript的面向对象模式与Java、C#等主流语言存在显著差异。对于熟悉Java或C#的开发者而言,理解以下两个核心概念至关重要:
1. 类(Class):类是对象的类型模板。例如,定义Student类代表学生类型,它定义了类型规范,但并不对应任何具体的学生个体。
2. 实例(Instance):实例是根据类创建的具体对象。例如,基于Student类可以生成xiaoming、xiaohong、xiaojun等多个实例,每个实例代表一个具体的学生,且均属于Student类型。
因此,类和实例是大多数面向对象编程语言的基础概念。
原型机制与类概念的区别
然而,在JavaScript中,这一概念需要调整。JS不严格区分类与实例,而是通过原型(prototype)机制来实现面向对象编程。
基于原型的对象创建与继承
当我们希望创建名为xiaoming的具体学生对象时,实际上并不存在一个预定义的Student类。此时,我们可以利用现有的对象作为基础。
var robot = {
name: 'Robot',
height: 1.6,
run: function () {
console.log(this.name + ' is running...');
}
};
观察这个robot对象,它拥有name和height属性,并具备run()方法,这与xiaoming的特征相似。我们可以直接基于该对象来“创建”xiaoming。
我们将该对象重命名为Student,并基于它创建xiaoming实例。
var Student = {
name: 'Robot',
height: 1.2,
run: function () {
console.log(this.name + ' is running...');
}
};
var xiaoming = {
name: '小明'
};
xiaoming.__proto__ = Student;
关键步骤在于最后一行代码:将xiaoming的原型指向Student对象。这使得xiaoming看起来像是从Student继承而来。
xiaoming.name; // '小明'
xiaoming.run(); // 小明 is running...
虽然xiaoming自身没有定义run()方法,但由于其原型链指向Student,且Student包含run()方法,因此xiaoming可以直接调用该方法。

JavaScript的原型链与Java的Class机制根本不同:JS没有“类”的概念,所有对象均为实例。所谓的继承,仅仅是将一个对象的原型指向另一个对象。
动态修改原型与最佳实践
如果将xiaoming的原型重新指向其他对象,其行为将随之改变。
var Bird = {
fly: function () {
console.log(this.name + ' is flying...');
}
};
xiaoming.__proto__ = Bird;
此时,xiaoming无法再调用run()方法,其行为特征已转变为类似Bird。
xiaoming.fly(); // 小明 is flying...
在JavaScript代码运行期间,你可以动态地将xiaoming的原型从Student切换为Bird,或指向任何其他对象。
上述代码仅用于演示原理。在实际开发中,不应直接使用obj.__proto__修改对象原型,且低版本IE浏览器不支持该属性。推荐使用Object.create()方法,它接受一个原型对象并返回基于该原型的新对象。由于新对象初始为空,我们可以封装一个函数来创建如xiaoming这样的实例。
// 原型对象:
var Student = {
name: 'Robot',
height: 1.2,
run: function () {
console.log(this.name + ' is running...');
}
};
function createStudent(name) {
// 基于Student原型创建一个新对象:
var s = Object.create(Student);
// 初始化新对象:
s.name = name;
return s;
}
var xiaoming = createStudent('小明');
xiaoming.run(); // 小明 is running...
xiaoming.__proto__ === Student; // true