今天学习了一些关于javascript面向对象的基础,之前在网上也陆续接触过一些,不过都感觉理解的不够透彻,所以今天做一个小结。

首先javascript的面向对象还要从何为对象说起,所谓对象可以看作是一个黑盒子,你并不清除它内部实现功能的原理,但是你只要了解它是如何使用的,并且能够用它自带的功能完成自己想要做的事情,基本上来说这就是面向对象的思想。其实面向对象的思想生活中随处可见,电视机,电冰箱,空调等等,都可以看作是对象,拿一般人来说,无需知道它们的工作原理,但只要按说明书去使用即可。

回归到javascript来说的话,在实际写代码的过程中,其实都已接触到面向对象,只不过很多时候是在使用上,使用一些预设的功能,比如用Date()创建日期对象再用其预设的方法getDate(),getFullYear()等可以实现调用时间日期等功能,Array()创建数组对象再用其预设的方法push(),pop(),shift(),splice()等可以实现数据存取等功能。而从使用面向对象,到自己写对象,是学习javascript必经的过程。 

自己写对象的话,还要从一些小知识点细细去说。

Step1:

变量---属性  

函数---方法

对于初学javascript的童鞋来说,声明变量/函数是家常便饭,但是可能对属性/方法的概念就比较模糊了。以下的代码可以加以说明:

/*变量与函数*/
var a = 1;    //声明一个变量并赋值为1
function b()  //声明一个函数b,它的功能为弹出变量a
{
  alert(a);
}
b();       //执行函数b

/*属性与方法*/
var obj = new Object();  //新建一个名为obj的空对象
obj.a = 1;          //给obj对象添加a属性并赋值为1
obj.b = function()    //给obj对象添加b方法,它的功能为弹出obj对象的属性a
{
  alert(obj.a); 
}
obj.b();          //执行obj对象的方法b

 

以上两段代码的执行结果都是一样,都是弹出1,只不过前者是以变量/函数实现,后者是属性/方法,其实说白了,变量之于属性,函数之于方法,是一个东西,只不过变量/函数是自由的,是不属于任何人的,而属性/方法是属于obj对象的。

PS:1.如果较真的话,其实变量/函数是若不是局部声明的话,默认为window对象的。2.系统自带的对象(如Array()对象等)也是可以添加属性与方法的,只不过这些对象已经有默认的属性与方法,若自行添加可能会覆盖原有的属性与方法,容易出问题,所以一般还是新建空白对象。

 

Step2:

this的指向

关于this的指向,说的再多不如一句话,不完全的说法:指代当前发生事件的对象,补充完全的说法:当前的方法属于谁那么就是指代谁。

一段代码说明如下:

var obj = new Object();
obj.a = 1;
obj.b = function()
{
  alert(this.a);
  console.log(this); 
}
obj.b();

可以看到这段与之前的属性与方法的代码大致一样,只不过将obj替换为this,执行结果一样,说明this指代的就是b方法的主人obj。

当然也可以在b方法中将this打印出来,结果其实就是obj对象。

 

Step3:

工厂模式

先看一段代码:

var a = new Object();      //新建对象a
a.name = 'mynamea';        //给a添加name属性并赋值'mynamea'
a.tel = '123456';          //给a添加tel属性并赋值'123456'
a.getName = function()     //给a添加getName方法弹出a的name属性的值
{
    alert(this.name);      //前面已经说过了this指向这个方法的主人a
}
a.getTel = function()      //给a添加getTel方法弹出a的tel属性的值
{
    alert(this.tel);       //this指向这个方法的主人a
}
a.getName();               //执行a的getName方法
a.getTel();                //执行a的getTel方法

以上代码运行后会弹出对象a的name及tel属性的值,姑且看做是这样一个功能。现在假设再加一个b对象,也需要这样的功能吧,代码就会变成:

var a = new Object();      //新建对象a
a.name = 'mynamea';        //给a添加name属性并赋值'mynamea'
a.tel = '123456';          //给a添加tel属性并赋值'123456'
a.getName = function()     //给a添加getName方法弹出a的name属性的值
{
    alert(this.name);      //前面已经说过了this指向这个方法的主人a
}
a.getTel = function()      //给a添加getTel方法弹出a的tel属性的值
{
    alert(this.tel);       //this指向这个方法的主人a
}
a.getName();             //执行a的getName方法
a.getTel();             //执行a的getTel方法

var b = new Object();      //新建对象b
b.name = 'mynameb';        //给b添加name属性并赋值'mynameb'
b.tel = '678910';          //给b添加tel属性并赋值'678910'
b.getName = function()     //给b添加getName方法弹出b的name属性的值
{
    alert(this.name);      //this指向这个方法的主人b
}
b.getTel = function()      //给b添加getTel方法弹出b的tel属性的值
{
    alert(this.tel);       //this指向这个方法的主人b
}
b.getName();           //执行b的getName方法
b.getTel();             //执行b的getTel方法

如此一来,我们最不想看到的事情发生了,相似的代码我们写了2遍,在编程中显然是不合理的,很自然的,我们想到了封装,经过初步处理,代码如下,也就是工厂模式:

//工厂模式
//这个函数内部创建了新对象,有这样的功能而被称为构造函数
function CreatDel(name,tel)        //传入参数name,tel
{
    //原料
    var obj = new Object();        //新建空对象obj
    //加工
    obj.name = name;               //给obj对象添加name属性赋值为参数name
    obj.tel = tel;                //给obj对象添加tel属性赋值为参数tel
    obj.getName = function()      //给obj对象添加getName方法弹出obj的name属性
    {
        alert(this.name);
    }
    obj.getTel = function()        //给obj对象添加getTel方法弹出obj的tel属性
    {
        alert(this.tel);
    }
    //出厂
    return obj;                    //将obj对象返回 作为CreatDel函数执行的返回
}

//调用
var a = CreatDel('mynamea','123456');    
a.getName();
a.getTel();

var b = CreatDel('mynameb','678910');
b.getName();
b.getTel();

以上运行结果与前面的两份代码一致,如此我们有CreatDel这个构造函数,相当于有了一个制造工厂,上面代码注释的部分已经讲明了工厂模式的运作原理。

但是工程模式其实是有弊端的:

1.不使用new。

2.函数重复的问题。

其中第一个问题确实是一个不是问题的问题,其解决办法也比较简单,代码处理如下,

//工厂模式
//这个函数内部创建了新对象,有这样的功能而被称为构造函数
//注意这时在调用的时候已经加new
function CreatDel(name,tel)        //传入参数name,tel
{
    //原料
    //加new后系统默认会做的事情:新建空对象this
    //即 var this = new Object();

    //加工
    //此时系统默认在原料部分新建了this对象,所以这里可以用this而不是obj了
    this.name = name;               //给this对象添加name属性赋值为参数name
    this.tel = tel;                //给this对象添加tel属性赋值为参数tel
    this.getName = function()      //给this对象添加getName方法弹出this的name属性
    {
        alert(this.name);
    }
    this.getTel = function()        //给this对象添加getTel方法弹出this的tel属性
    {
        alert(this.tel);
    }

    //出厂
    //加new后系统默认会做的事情:返回this对象 作为CreatDel构造函数执行的返回
    //即 return this;
}

//调用
//调用时加new
var a = new CreatDel('mynamea','123456');    
a.getName();
a.getTel();

var b = new CreatDel('mynameb','678910');
b.getName();
b.getTel();

运行结果与前面一致,这样也算是简化了代码了。

第二个问题,函数重复的问题,其实看上面的代码就应该可以看出端倪,a对象也好b对象也罢,都拥有同样的getName与getTel方法,那么他们同名的方法是同一个方法吗,我们将代码注释去掉,然后写一个东西来验证它:

function CreatDel(name,tel)
{
    this.name = name;
    this.tel = tel;
    this.getName = function()
    {
        alert(this.name);
    }
    this.getTel = function()
    {
        alert(this.tel);
    }

}

var a = new CreatDel('mynamea','123456');    
a.getName();
a.getTel();

var b = new CreatDel('mynameb','678910');
b.getName();
b.getTel();

alert(a.getName==b.getName);  //比较a的getName方法与b的getName方法
alert(a.getTel==b.getTel);   //比较a的getTel方法与b的getTel方法

最后两行代码为比较这两个对象所带的同名方法是否为同一方法,结果都返回false。

显而易见,不是同一个方法,那若是需要千千万万类似的对象,那么即会产生千千万万个同样功能的方法,极大的浪费系统资源显然这样的状况我们是不愿意看到的。

那么怎么办呢,到这一步,我们需要请出prototype。

在这里说的通俗易懂一些的话,prototype是类添加方法的,即a对象及b对象都是由CreatDel这个构造函数通过工厂模式制造出来的,可以说CreatDel就是a,b对象的模子,那么我在模子上加一个东西,那么制造出来的对象必定也会带有同样的东西,即带有同样的方法了。好的,代码接着修改如下:

function CreatDel(name,tel)    //这里其实CreatDel可以看作是一个模子/一个类
{
    this.name = name;
    this.tel = tel;
}

CreatDel.prototype.getName = function()    //通过prototype添加方法getName
{
    alert(this.name);
}

CreatDel.prototype.getTel = function()    //通过prototype添加方法getTel
{
    alert(this.tel);
}

var a = new CreatDel('mynamea','123456');    
a.getName();
a.getTel();

var b = new CreatDel('mynameb','678910');
b.getName();
b.getTel();

alert(a.getName==b.getName);       //比较a的getName方法与b的getName方法
alert(a.getTel==b.getTel);        //比较a的getTel方法与b的getTel方法

这个时候发现最后两行代码返回已经为true了,说明两个对象拥有了同一个方法。

通过prototype为CreatDel这个类添加新的方法,然后再通过CreatDel制造出对象a跟b,那么这两个对象在拥有不同属性的同时,可以拥有同样的方法,姑且可以作为javascript面向对象基础的经典案例了。