今天学习了一些关于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面向对象基础的经典案例了。