JavaScript设计模式:一、面向对象编程
JavaScript面向对象编程
众所周知,JS作为一门脚本语言,由于其设计者在设计JS的时候,也仅仅用了很少的时间就完成了JS这门语言的创建,JS虽然拥有着脚本语言的优势,但是JS也存在着天生的缺陷。其中之一就是:“没有完整的面向对象和自定义类型支持”,这是因为JS本身没有很好的模块化。但事实上是,很多JS学习者或者使用者的共同点是:他们接触的第一门编程语言大都是C++或者Java这种老牌OOP语言,写代码时都是秉持着面向对象的思想,这在学习JS或者使用JS不免会感到有一些难受。
那么JS可不可以实现面向对象编程,或者说,在某种程度上做到面向对象编程?答案是肯定的。
一、用对象收编函数与变量
我们先看三个函数:
1 function fn1 () { 2 // ... 3 } 4 5 function fn2 () { 6 // ... 7 } 8 9 function fn3 () { 10 // ... 11 }
这段代码有一个问题就在于:定义了三个全局函数,在一个项目里,这是不提倡的事实上也是不被允许的,创建全局函数或者变量,意味着在以后的代码里,这些函数或者变量就有与他人的代码产生冲突的可能。那么怎么解决这一类的问题呢?
我们知道,对象可以拥有自己的属性与方法,并且在我们需要使用这些属性和方法的时候,通过点语法来使用。可不可以利用对象来收编这些函数或者变量呢? 看下面的代码:
1 var obj = { 2 fn1: function () { 3 // ... 4 } 5 fn2: function () { 6 // ... 7 } 8 fn3: function () { 9 // ... 10 } 11 }
通过创建一个这样的对象,我们就可以通过obj.fn1的方式,来调用对应的方法,并且这些方法是存在于obj对象上的。
我们再看看下面的这种写法(与上面的写法其实是一种意思):
1 var Obj = function () {} 2 Obj.fn1 = function () { 3 // ... 4 } 5 Obj.fn2 = function () { 6 // ... 7 } 8 Obj.fn3 = function () { 9 // ... 10 }
上面的方法存在着另外一个问题,当别人需要使用我们的对象方法的时候就有一些麻烦了,因为这个对象并不能被复制一份或者说我们通过 new 关键字
var test = new Obj() ,所创建的新对象test,并不拥有fn1,fn2,fn3这些方法。
要想解决这个问题,我们可以这么写:
1 var Obj = function () { 2 return { 3 fn1: function () { 4 // ... 5 } 6 fn2: function () { 7 // ... 8 } 9 fn3:function () { 10 // ... 11 } 12 } 13 }
这样写有什么作用呢?可以看出,每次调用该函数时,都会返回一个新对象,新对象拥有fn1,fn2,fn3这三个方法。这样,每个人在使用时就互不影响了。
1 var test = Obj() 2 test.fn1()
我们虽然通过返回一个新对象在一定程度上解决了他人使用的问题,但这并不是真正意义上的类的创建方式(相信Java或者C++使用者深有体会),并且我们上面创建的对象test与Obj没有任何的关系,所以,我们再次换一种写法:
1 var Obj = function () { 2 this.fn1 = function () { 3 // ... 4 } 5 this.fn1 = function () { 6 // ... 7 } 8 this.fn1 = function () { 9 // ... 10 } 11 }
我们在这个函数内,使用了this关键字,像上面这样的一个对象Obj,我们就可以将其看成一个类了,拥有三个成员函数:fn1,fn2,fn3,既然是一个类,那么使用的时候就要通过new关键字来创建对象了:
1 var test = new Obj() 2 test.fn1()
另一个问题出现了,每一个使用new关键字所创建出来的新对象,通拥有属于自己的一套方法,但事实上,这并不是必要的,这会造成内存不必要的消耗。
怎么解决这个问题呢?不知道读者是否还记得JS中的每一个函数,都有一个prototype对象,我们称之为“原型”,原型上的属性和方法是new出来的对象所共享的,我们是否可以将公共的函数转移到prototype上呢?
1 var Obj = function () {} 2 Obj.prototype.fn1 = function () { 3 // ... 4 } 5 Obj.prototype.fn2 = function () { 6 // ... 7 } 8 Obj.prototype.fn3 = function () { 9 // ... 10 }
这样我们以后通过new关键字创建的对象实例,所拥有的都是共同的方法,因为他们都要依赖prototype原型依次寻找,找到的方法都是同一个。
以上的这种方式,还有另一个写法:
1 var Obj = function () {} 2 Obj.prototype = { 3 fn1: function () { 4 // ... 5 } 6 fn2: function () { 7 // ... 8 } 9 fn3: function () { 10 // ... 11 } 12 }
需要注意的是,这两中方式并不能混用,因为如果在后面为对象的原型对象赋值为新对象的时候,它会覆盖掉之前对prototype对象赋值的写法。