本文深入解析如何在JavaScript中实现面向对象编程。通过function关键字定义类,利用var和this区分私有与公有属性及方法。文章详细演示了如何模拟构造函数、处理带参构造,并重点解决了事件回调中this指针指向错误的问题,提供实用的代码实现方案。
对象泛指人们研究的任何事物,无论是简单的整数还是复杂的飞机,甚至抽象的规则、计划或事件均可视为对象。
面向对象编程是目前主流的编程范式,但令人遗憾的是,前端开发中最常用的JavaScript语言原生并不直接支持这一特性。
JavaScript缺乏访问控制符、class关键字、继承关键字extend或冒号,以及虚函数virtual,但这门灵活的语言允许我们通过其他方式实现类的定义与对象创建。
定义类并创建实例
接下来我们将探讨如何定义类并创建类的实例对象。
在JavaScript中,我们可以使用function关键字来定义类,具体示例如下。
代码如下:
function Shape()
{
var x=1;
var y=2;
}
这看起来像是在定义函数,确实如此,我们定义了一个名为Shape的函数并初始化了x和y。但从另一个角度看,这等同于定义了一个包含x和y两个属性(初始值分别为1和2)的Shape类,只是定义类的关键字换成了function而非class。
随后,我们可以创建Shape类的实例对象aShape,代码如下。
代码如下:
var aShape = new Shape();
公有与私有属性定义
下面介绍如何定义类的公有属性和私有属性。
虽然我们创建了aShape对象,但直接访问其属性时会报错,例如尝试执行aShape.x=1;时会出现错误。
代码如下:
aShape.x=1;
这表明使用var定义的属性是私有的。若要定义公有属性,必须使用this关键字。
代码如下:
function Shape()
{
this.x=1;
this.y=2;
}
这样即可访问Shape的属性,例如执行aShape.x=2;。
代码如下:
aShape.x=2;
总结上述代码可知:使用var可定义类的私有属性,而使用this可定义类的公有属性。
公有与私有方法定义
接下来讨论公有方法和私有方法的定义。
在JavaScript中,函数是Function类的实例,间接继承自Object,因此函数也是对象。我们可以通过赋值方式创建函数,或将函数赋给类的属性变量,从而使其成为可执行的方法。
代码如下:
function Shape()
{
var x=0;
var y=1;
this.draw=function()
{
//print;
};
}
我们在上述代码中定义了draw方法,并将一个function赋值给它。随后,我们可以通过aShape调用该函数,这在面向对象编程中称为公有方法,例如执行aShape.draw();。
代码如下:
aShape.draw();
若使用var定义draw,则该函数变为私有方法,例如尝试通过aShape.draw调用时会失败。
代码如下:
function Shape()
{
var x=0;
var y=1;
var draw=function()
{
//print;
};
}
此时无法通过aShape.draw调用该函数。
构造函数与带参构造
关于构造函数的实现。
JavaScript原生不支持面向对象,因此也没有构造函数,但我们可以模拟一个在对象创建时自动调用的构造函数,代码如下。
代码如下:
function Shape()
{
var init = function()
{
//构造函数代码
};
init();
}
在Shape函数末尾手动调用init函数,这样每次创建Shape对象时init都会自动执行,从而模拟构造函数。
关于带参数的构造函数。
实现带参构造函数很简单,只需将传入参数写入函数参数列表即可,例如。
代码如下:
function Shape(ax,ay)
{
var x=0;
var y=0;
var init = function()
{
//构造函数
x=ax;
y=ay;
};
init();
}
这样我们就可以通过以下方式创建对象。
代码如下:
var aShape = new Shape(0,1);
静态属性与方法
关于静态属性和静态方法的定义。
在JavaScript中定义静态属性和方法的方法如下所示。
代码如下:
function Shape(ax,ay)
{
var x=0;
var y=0;
var init = function()
{
//构造函数
x=ax;
y=ay;
};
init();
}
Shape.count=0;//定义一个静态属性count,这个属性是属于类的,不是属于对象的。
Shape.staticMethod=function(){};//定义一个静态的方法
有了静态属性和方法,就可以通过类名访问它们,例如。
代码如下:
alert ( aShape.count );
aShape.staticMethod();注意:静态属性和方法目前均为公有,尚不清楚如何将其设为私有。
方法内属性访问与this指针处理
在方法中访问本类的公有属性和私有属性。
在类的方法中访问自身属性时,JavaScript对公有和私有属性的访问方式不同,请看以下代码。
代码如下:
function Shape(ax,ay)
{
var x=0;
var y=0;
this.gx=0;
this.gy=0;
var init = function()
{
x=ax;//访问私有属性,直接写变量名即可
y=ay;
this.gx=ax;//访问公有属性,需要在变量名前加上this.
this.gy=ay;
};
init();
}
关于this的注意事项。
根据经验,类中的this并不始终指向当前对象,主要原因是JavaScript非原生面向对象语言,且函数和类均使用function定义,这可能导致一些问题。
this指针出错通常发生在事件处理中。当希望某个对象的成员函数响应事件时,系统调用该成员函数,但传入的this指针已不再是原对象,此时在成员函数中调用this会出错。
解决方法是在定义类时将this保存到一个私有属性中,后续用该属性替代this。这种方法使用this指针较为安全且省心。
我们修改代码以解决this问题,对照第六部分的代码即可理解。
代码如下:
function Shape(ax,ay)
{
var _this=this; //把this保存下来,以后用_this代替this,这样就不会被this弄晕了
var x=0;
var y=0;
_this.gx=0;
_this.gy=0;
var init = function()
{
x=ax;//访问私有属性,直接写变量名即可
y=ay;
_this.gx=ax;//访问公有属性,需要在变量名前加上this.
_this.gy=ay;
};
init();
}
以上讨论了如何在JavaScript中定义类、创建对象、定义公有和私有属性及方法、创建静态属性和方法、模拟构造函数,并探讨了易错的this指针。
关于JavaScript中面向对象实现的讨论至此结束,上述内容最为实用,通常足以满足JavaScript定义类和创建对象的需求。当然,也可以使用mootools或prototype等框架,它们对类模拟更完善,支持继承。若使用框架需引入相关js文件,但掌握无框架下的类创建能提高效率,且可见创建简单类并不复杂。