本文深入解析JavaScript面向对象编程的核心机制。从基础函数即对象的概念出发,详细演示如何利用prototype定义实例属性与方法,探讨通过闭包实现私有成员变量的技巧,并讲解静态属性与静态方法的定义及访问限制。结合Java对比,帮助开发者掌握规范化的JS大型应用开发模式。
JavaScript面向对象编程背景与必要性
在AJAX技术普及之前,JavaScript代码往往缺乏规范,开发者通常想到什么就写什么,仅依赖一个个独立的function函数。遇到重复逻辑时只能简单复制,一旦函数名冲突,排查错误变得异常困难。由于长期采用面向过程的编程思维,加之网络充斥着大量随意且不规范的小型JS代码片段,导致公众对JavaScript产生误解,认为它仅适合辅助性的小功能,无法胜任大型应用的开发。然而,随着AJAX的兴起,对JavaScript代码的需求日益增长,要求开发者具备类似Java的面向对象开发能力。
接下来,结合个人经验与学习心得,共同探讨如何在JavaScript中实现面向对象编程。实际上,利用JS进行面向对象开发并非难事,因为在JavaScript中,每一个function本质上就是一个对象。例如,定义如下一个函数:
代码如下:
function HelloWorld()
{
alert('hello world!');
} 在使用时,我们可以将其视为一个对象来调用。例如,通过以下测试函数:
代码如下:
function _test()
{
var obj = new HelloWorld();
} 基于Prototype的实例属性与方法定义
当调用_test方法后,会弹出包含'hello world!'的提示框,这实际上是调用了HelloWorld()对象(即函数)。此时,HelloWorld对象没有任何额外属性和方法,仅包含一个构造方法HelloWorld()。我们可以将其类比为Java中一个没有任何属性和方法的类,当使用new关键字创建对象时,即调用其构造方法。这是最简单的对象形式。当然,一个完整的对象应当具备属性和方法,在JS中,我们使用prototype原型关键字来赋值。例如,若需给HelloWorld对象增加一个sayHello方法和一个name属性,可按如下方式添加:
代码如下:
HelloWorld.prototype = {
name : 'JavaScript',
sayHello : function() {
alert(this.name);
}
} 如此便为HelloWorld添加了name属性和sayHello方法。接着修改_test方法,如下:
代码如下:
function _test()
{
var obj = new HelloWorld();
obj.sayHello();
} 调用_test方法后,将先后打印'hello world!'和'JavaScript'(前者来自构造方法中的alert,后者来自sayHello方法中的alert)。需注意,sayHello方法中的alert引用了this关键字,该关键字表示的就是HelloWorld对象,即默认指向当前对象,这与Java中的this关键字作用一致。
对于向对象添加实例方法和属性,可采用上述方式,即使用prototype关键字进行赋值,格式如下:
代码如下:
对象名称.prototype = {
属性一 : 属性值,
属性二 : 属性值,
方法一 : function(参数列表) {
方法体;
},
方法二 : function(参数列表) {
方法体;
}
} 按照上述方式,可对一个对象定义多个属性和方法。在new一个对象后,即可使用实例名称.属性或方法来获取属性或执行方法。
利用闭包实现私有成员变量
在上述方法中,大家可能注意到对象的属性可以直接访问,例如访问HelloWorld对象的name属性可使用obj.name直接获取。这类似于Java中的公有属性,且我们可以直接对name属性进行赋值操作。现在提出一个问题:如何给对象赋予私有成员变量?这就需要修改HelloWorld类的声明方式,不再使用prototype进行属性和方法声明,而是直接使用内嵌函数和属性进行声明。修改后的HelloWorld如下,我们将其命名为HelloWorld2:
代码如下:
function HelloWorld2()
{
var privateProp = 'hello world 2!';
this.method = function() {
alert(privateProp);
}
} 观察HelloWorld2的类声明方式,是在函数内部进行了函数嵌套声明,并设置了一个局部变量privateProp,即我们的私有成员变量。该变量只能被HelloWorld2内部的函数访问,外部无法访问,从而通过变量的作用域巧妙设置了类的私有变量。应用如下:
代码如下:
function _test2()
{
var obj2 = new HelloWorld2();
obj2.method(); // 调用该方法将打印'hello world 2!
alert(obj2.privateProp); // 将打印undefined
} 上述内容主要讲解如何定义类的实例方法和属性。由于采用prototype方式进行定义清晰明了,因此通常使用该方式定义类,且许多AJAX框架中均使用了类似的类声明方式。然而,类的私有成员变量只能在类的构造方式中的函数进行访问,这样类的prototype声明的方法就不能访问该私有成员变量,且可读性方面也没有prototype方式好。
静态属性与静态方法的定义及访问
以上讨论的是定义类的实例方法和属性。在Java中,类分为实例方法和属性与类方法和属性。所谓类属性和方法,是指该类的所有实例只维护一份类属性和类方法的副本,而非每个实例都维护一套,这与实例属性和实例方法不同。那么在JS中如何为类定义静态类方法和类属性呢?我们可以直接为类添加静态属性和静态方法,例如为HelloWorld类添加一个age的静态属性和一个hello的静态方法,声明如下:
代码如下:
HelloWorld.age = 22;
HelloWorld.hello = function() {
alert(HelloWorld.age);
} 这样便为类HelloWorld声明了静态属性age和静态方法hello。在使用时,直接使用类名进行访问,但不能使用实例进行访问,这点与Java一致。测试如下:
代码如下:
function _test()
{
var obj = new HelloWorld();
obj.sayHello(); // 正确,实例方法,可以通过实例进行访问
HelloWorld.hello(); // 正确,静态方法,通过类名进行直接访问
obj.hello(); // 错误,不能通过实例访问静态方法。会报JS错误!
} 通过上述说明,相信大家对JavaScript进行面向对象编程已有一定了解,或许已跃跃欲试。不妨尝试一下(提示:以上代码全部通过测试!)