JavaScript面向对象编程:类式与原型式的深度解析
本文深入解析JavaScript的面向对象机制,对比基于类的面向对象与基于原型的面向对象差异。详细讲解使用构造函数和字面量两种创建对象的方法,分析this关键字、属性赋值及实例化的区别,帮助开发者根据实际需求选择最优方案。
重新认识面向对象
深入理解面向对象编程 为论证 JavaScript 作为纯面向对象语言的地位,需从面向对象核心概念入手,解析以下关键点:
- 万物皆为对象
- 对象具备封装与继承机制
- 对象间通过消息进行通信,且各自实现信息隐藏
基于上述三点,C++ 被视为半面向对象半面向过程语言,因其虽实现了类的封装、继承及多态,但仍存在非对象形式的全局函数与变量。Java 与 C# 则是完全的面向对象语言,它们通过类来组织函数和变量,确保其无法脱离对象独立存在。然而,函数本身仍是一个过程,仅依附于特定类上。
类式与原型式面向对象比较
然而,面向对象仅是一种概念或编程思想,不应依赖于特定语言。例如 Java 采用面向对象思想构建语言,实现了类、继承、派生、多态及接口等机制。但这些机制仅是实现面向对象编程的手段,而非必要条件。换言之,一门语言可根据自身特性选择合适方式来实现面向对象。鉴于多数程序员首先学习或使用类似 Java、C++ 等高级编译型语言(Java 虽为半编译半解释,但通常按编译型讲解),他们往往先入为主地接受了“类”这一面向对象实现方式,从而在学习脚本语言时,习惯性地用类式面向对象语言的概念来判断该语言是否具备面向对象特性。这也是阻碍程序员深入学习并掌握 JavaScript 的重要原因之一。 实际上,JavaScript 通过一种称为原型(prototype)的方式来实现面向对象编程。下文将探讨基于类的(class-based)面向对象与基于原型的 (prototype-based) 面向对象在构建客观世界方式上的差异。 基于类的面向对象与基于原型的面向对象方式比较 在基于类的面向对象方式中,对象(object)由类(class)生成。而在基于原型的面向对象方式中,对象(object)则由构造器(constructor)利用原型(prototype)构建。以客观世界的例子说明两种认知方式的差异:例如工厂制造汽车,一方面工人需参照工程图纸,规定汽车制造规范。这里的工程图纸类似语言中的类 (class),而汽车则是按此类 (class) 制造出来的;另一方面,工人和机器(相当于 constructor)利用各种零部件如发动机、轮胎、方向盘(相当于 prototype 的各个属性)将汽车构造出来。 事实上,关于这两种方式谁更彻底地表达面向对象思想,目前仍有争议。但笔者认为原型式面向对象是一种更为彻底的面向对象方式,理由如下: 首先,客观世界中的对象产生都是其他实物对象构造的结果,而抽象的“图纸”无法直接产生“汽车”,即类是一个抽象概念而非实体,对象的产生则是实体的产生; 其次,依据一切事物皆对象这一基本面向对象法则,类 (class) 本身并非一个对象,然而原型方式中的构造器 (constructor) 和原型 (prototype) 本身也是其他对象通过原型方式构造出来的对象。 再次,在类式面向对象语言中,对象的状态 (state) 由对象实例 (instance) 持有,对象的行为方法 (method) 则由声明该对象的类所持有,且只有对象的结构和方法能够被继承;而在原型式面向对象语言中,对象的行为、状态均属于对象本身,并且能够一起被继承(参考资源),这也更贴近客观实际。 最后,类式面向对象语言如 Java,为弥补无法使用面向过程语言中全局函数和变量的不便,允许在类中声明静态 (static) 属性和静态方法。而实际上,客观世界不存在所谓静态概念,因为一切事物皆对象!而在原型式面向对象语言中,除内建对象 (build-in object) 外,不允许全局对象、方法或者属性的存在,也没有静态概念。所有语言元素 (primitive) 必须依赖对象存在。但由于函数式语言的特点,语言元素所依赖的对象是随着运行时 (runtime) 上下文 (context) 变化而变化的,具体体现在 this 指针的变化。正是这种特点更贴近 “万物皆有所属,宇宙乃万物生存之根本”的自然观点。
JavaScript面向对象基础知识
JavaScript 面向对象基础知识
尽管 JavaScript 本身没有类的概念,但它仍具备面向对象特性,尽管与一般常见的面向对象语言有所差异。
创建对象的方法
创建对象的一种简单方法如下:
function myObject() {
};
JavaScript 中创建对象的方法一般来说有两种:函数构造法和字面量法,上面这种属函数构造法。下面是一个字面量法的例子:
var myObject = {
};
如果仅需一个对象,而不需要对象的其它实例的情况下,推荐使用字面量法。如果需要对象的多个实例,则推荐函数构造法。 定义属性和方法
函数构造法:
function myObject() {
this.iAm = 'an object';
this.whatAmI = function() {
console.log('I am ' + this.iAm);
};
};
字面量法:
var myObject = {
iAm : 'an object',
whatAmI : function() {
console.log('I am ' + this.iAm);
}
};
以上两种方法创建的对象中,均包含一个名为 “iAm” 的属性和一个名为 “whatAmI” 的方法。属性是对象中的变量,方法则是对象中的函数。
如何获取属性及调用方法:
var w = myObject.iAm;
myObject.whatAmI();
两种创建对象方法的区别
调用方法时必须加上括号,如果不加括号,则仅返回方法的引用。 两种创建对象方法的区别
- 函数构造法中定义属性和方法时需使用前缀 this,字面量法则不需要。
- 函数构造法给属性和方法赋值时使用 =,字面量法使用 : 。
- 若有多个属性或方法,函数构造法内部用 ; 隔开,字面量法用 , 隔开。
对于字面量法创建的对象,可直接通过对象引用调用其属性或方法:
myObject.whatAmI();
而对于函数构造法而言,需创建对象实例,才能调用其属性或方法:
var myNewObject = new myObject();
myNewObject.whatAmI();
使用构造函数
使用构造函数
现在回顾之前的函数构造法:
function myObject() {
this.iAm = 'an object';
this.whatAmI = function() {
console.log('I am ' + this.iAm);
};
};
它看起来像个函数,既然是函数,能否传递参数呢?对代码稍作修改:
function myObject(what) {
this.iAm = what;
this.whatAmI = function(language) {
console.log('I am ' + this.iAm + ' of the ' + language + ' language');
};
};
再将对象实例化,并传入参数:
var myNewObject = new myObject('an object');
myNewObject.whatAmI('JavaScript');
两种创建对象的方法,我该用哪种?
程序最终输出 I am an object of the JavaScript language。 两种创建对象的方法,我该用哪种?
对于字面量方法而言,因其无需实例化,若修改某对象的值,则该对象的值将被永久修改,其他任何地方再访问,均为修改后的值。而对于函数构造法而言,修改值时是修改其实例的值,它可以实例化 N 个对象,每个对象均可拥有自己不同的值,且互不干扰。比较以下几段代码。
先看字面量法:
var myObjectLiteral = {
myProperty : 'this is a property'
};
console.log(myObjectLiteral.myProperty); // log 'this is a property'
myObjectLiteral.myProperty = 'this is a new property';
console.log(myObjectLiteral.myProperty); // log 'this is a new property'
即便创建了一个新变量指向该对象,结果依然相同:
var myObjectLiteral = {
myProperty : 'this is a property'
};
console.log(myObjectLiteral.myProperty); // log 'this is a property'
var sameObject = myObjectLiteral;
myObjectLiteral.myProperty = 'this is a new property';
console.log(sameObject.myProperty); // log 'this is a new property'
再看函数构造法:
// 用函数构造法
var myObjectConstructor = function() {
this.myProperty = 'this is a property'
};
// 实例化一个对象
var constructorOne = new myObjectConstructor();
// 实例化第二个对象
var constructorTwo = new myObjectConstructor();
// 输出
console.log(constructorOne.myProperty); // log 'this is a property'
// 输出
console.log(constructorTwo.myProperty); // log 'this is a property'
和预期一样,两个对象的属性值是一样的。如果修个其中一个对象的值呢?
// 用函数构造法
var myObjectConstructor = function() {
this.myProperty = 'this is a property';
};
// 实例化一个对象
var constructorOne = new myObjectConstructor();
// 修改对象的属性
constructorOne.myProperty = 'this is a new property';
// 实例化第二个对象
var constructorTwo = new myObjectConstructor();
// 输出
alert(constructorOne.myProperty); // log 'this is a new property'
// 输出
alert(constructorTwo.myProperty); // log 'this is a property'
可见,用函数构造法实例化出的不同对象相互独立,可各自拥有不同的值。因此,具体采用哪种方法创建对象,需视实际情况而定。
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
