让js具有面向对象的特性

来源:互联网 发布:离线阅读小说软件 编辑:程序博客网 时间:2024/06/06 00:36

需求分析:使用javascript时,难免会用到复杂的数据结构,当通过定义对象和方法无法解决问题时,就要考虑使用面向对象的方法

困难:js不支持类的定义,也就是说我们无法通过定义一个类来封装我们想要的属性和方法,可以直接定义一个对象而无需构造函数.

变通:对象中可以任意存放属性和方法,那么就可以使用对象来模拟出类

在使用js的时候,想必大家都是用过类似于以下形式的方式来为设置对象:

var myObj={};myObj.key = "value";myObj.func = function(key,value){myObj.key = value;}
以上代码我们就定义了一个对象myObj,并且为其定义了一个属性key值为value,定义了一个方法function,然后我们可以做一下测试:

myObj.func("key","value2");alert(myObj.key);

结果是什么呢?value2

可以看到我们成功定义了一个对象的属性和方法,并通过方法修改了对象的属性.但这里有一个很大的缺点,这个对象只能"自我陶醉",无法像如果需要另外定义一个对象,赋予相同的属性和方法,就需要重复以上的步骤.而面向对象中,类和对象的区别就在于,类是对象的模板,可以使用构造方法来定义一系列具有相同属性和方法的对象.那么模仿面向对象,我们作如下改进:

function myObj(key,value){this.key = key;this.value = value;this.func = function(key){if(this.key ==key ){return this.value;}else{return null;}}}
该方式是基于定义方法的模式来模拟一个类,那么该如何来使用呢?

var obj = new  myObj("key","value");alert(obj.func("key"));
看一看是不是特别像java里面的,定义一个类,然后使用构造方法定义对象,之后调用get方法返回value;

在这里有必要说一下:myObj相当于类名,myObj(key,value)相当于构造方法,只是融为一体了.使用this修饰的属性或方法都是public类型的,定义完以后可以通过对象访问.

如果需要定义私有的属性,可以使用var来修饰.

这个方法是我目前最常用的方式.
然而如果想要使用更多的面向对象的特性,比如继承,多态,可以用更好的办法来定义类.

使用"构造函数法"最大的问题是创建对象时需要用new来创建,这样的方式虽然好用,却是一种浪费内存的行为,也就是说,每创建一个对象,所有的属性和方法都会重新分配内存,其实使用js模拟类时毕竟不是真正的类,有些属性和方法都可以是公用的,这让我们想到了java的单例模式和静态变量.

javascript规定,所有的构造函数都有一个属性,prototype指向另一个对象,该对象下的所有属性和方法都会被构造函数继承,也就是说所有的static 的属性和方法我们可以定义在prototype上面,例如:

function myObj(key,value){this.key = key;this.value = value;this.func = function(key){if(this.key ==key ){return this.value;}else{return null;}}}myObj.prototype.type = "staticType";myObj.prototype.get = function(){}

这样无论创建多少个对象,他们拥有的属性type和方法get都是共享的了。

使用this和prototype时会把代码的结构搞的有些混乱,那些对js不是非常熟练的同学们就无法读懂和使用这些代码,那么怎么样进行改进让我们更易使用呢?

这里推荐一种极简主义方法,也就是抛弃this和prototype关键字,模仿单例模式创建对象。

例如:要创建一个Animal类:

var Animal={    getInstance:function(){var animal = {};animal.name="凯特";animal.run = function(){alert("100km/h");}; return animal;    }};


原理很简单,对象里面定义了一个方法属性getInstance,该方法就是我们在java中常见的单例模式,也可以理解为工厂模式。如果想要创建对象,就去调用构造方法:

var a = Animal.getInstance();a.run();
Animal跟类十分相似吧,getInstance就是个静态方法。

面向对象封装性已经说过了,以下说一说继承

再定义一个类(对象):

var Cat = {    getInstance:function(){var cat= Animal.getInstance();cat.name="kite";cat.catchMouse = function(){    alert("can do");}; return cat;    }};
类Cat拥有一个相同的构造方法getInstance,只是Cat创建对象的时候是用Animal为模板创建的,再加上自己的属性和方法,就实现了继承,测试一下:

var cat1 = Cat.getInstance();cat1.run();cat1.catchMouse();
可以证明,我们确实可以调用run()和catchMouse()也就是说继承的问题得到解决了,那么多态呢?子类是否能够覆盖父类的方法呢,子类的属性能否重写父类的属性呢?

继续测试:

alert(cat1.name);
可以得出结论,cat1.name的值已经被替换了,至于方法当然也是这般。下面我们需要考虑一个问题,如何获得共享的属性,就像Java里面的static属性一样(java中的static变量属于类变量,不提倡通过对象调用,但可以调用,所有的对象调用都是同一状态),我们该怎么做呢?

var Cat = {legNum:4,    getInstance:function(){var cat= Animal.getInstance();cat.name="kite";cat.catchMouse = function(){    alert("can do");}; cat.getLegNum = function(){cat.legNum = Cat.legNum;}cat.changeLegNum = function(legNum){Cat.legNum = legNum;}return cat;    }};



测试一下legNum属性是不是共享的:

var cat1 = Cat.getInstance();var cat2 = Cat.getInstance();alert(cat1.getLegNum()==cat2.getLegNum());cat1.changeLegNum(3);alert(cat1.getLegNum()==cat2.getLegNum());
可以看到两次打印出来的结果都是true,数据已经实现了共享,要注意,共享变量时依附于类的,在实现数据共享的过程中,要获得类的变量,修改变量也是最终实现类变量的修改。





0 0
原创粉丝点击