js从诞生到现在一路与时俱进,现在我来讲一下关于js的面向对象编程。
首先要想清楚js什么是面向对象编程。一下是维基百度对面向对象的解释:
简单来说:就好比你有一个手机,你不了解他的工作原理,但是手机有一些按钮,你只需要知道这些按钮有什么用的就可以了。那这样说的话,对象就好比一个黑盒子,你不知道对象里的内部结构,但是却知道它的各种操作就可以了。
面向对象里包含三个东西,一个是抽象,一个是封装,另一个是继承。
抽象指的是把一个事物最主要最核心的东西抽取出来。比如人来说,人的属性有身高、年龄、性别、姓名、配偶等等,但是如果是我们开发一个学生管理系统,像配偶、身高等之类无关紧要的东西都可以不做考虑,我们只是关心与这个系统相关的东西,比如姓名、学号、班级等等,这就是抽象。抽象的过程其实是把一个对象最主要的特征及跟问题相关的给特征抽取出来。
封装指的是把一个对象内部的细节给封装起来,用这个对象的人是不需要关心这个对象具体如何实现,只需要关心如何去用就可以了,这就好比我上面那个手机的例子。
继承分有两种,一种是多重继承,另一种是多态。继承它可以保证让我们写的代码保持最大限度的重用,它是从父类中继承出一些方法和属性,子类可以有一些自己的特性。
多重继承是一个子类可以继承多个父类,但是多重继承有一个不足就是会让子类比较混乱。多态这东西对于js这个弱类型语言来说意义没那么大,我下篇文章会讲到多态。下面来讲对象的组成。
对象是由方法和属性这两个东西来组成的。可能有人会觉得方法就是函数,属性就是变量,这两类东西还是有些区别的。下面来讲例子:
首先是属性和变量的区别。下面的代码我声明了一个变量a,赋值为10,然后我又声明了一个数组对象arr,然后在arr对象中声明一个属性10,。
var a=10; alert(a); var arr=[1,2,3]; arr.a=10; alert(arr.a);这样代码一运行会输出两个弹框,都是10。乍一看变量能做的事,属性也能做,比如arr.a++,照样会自增1,但是变量比属性自由,属性是属于对象中,不能脱离声明他的对象,而变量a不属于arr对象。同理,函数与方法的区别也是一样。方法是写在对象里面的,而函数却是不属于方法的对象里。
在面向对象中,有一个this关键字,this关键字指的是当前的对象。如下面这个例子:
var arr=[1,2,3]; arr.a=10; arr.show=function () { alert(this.a) } arr.show();arr对象有一个show方法,方法里面有一个alert弹出的是this.a,this.a等于10,这个this指的就是arr对象。那如果是下面这样子的话,this又会指的是什么呢?
function show() { alert(this) } show()可能有基础的就会这个,这个this指的是window。因为在DOM中,window是属于根对象,全局的方法都是在window之下的,所以上面的例子也可以写成这样:
window.show=function () { alert(this) } show()
当然,讲了那么久,应该开始写js面向对象的代码了,js面向对象有两种写法,一种是基于Object的形式,另一种是对象字面量的方式(有点类似json),首先来写一个基于object形式的demo,最后会输出一个1和一个2:
var a=new Object(); a.num=1; //添加一个num属性 alert(a.num); a.add=function () { //添加一个add方法 this.num++ } a.add(); alert(a.num);上述demo的对象字面量写法是这样子的:
var a={ num:1, //添加一个num属性 add:function () { //添加一个add方法 this.num++ } }; alert(a.num); a.add(); alert(a.num);
上面例子只是讲了如果只调用该对象一次而已,如果是多次调用的话,则要封装起来。下面的两个demo,一个是用Object写的,另一个是对象字面量写的,这两种都是基于工厂模式(这个工厂模式和设计模式的工厂模式不同概念),在这个工厂模式下,可以不需要this来接收参数,他的原理有点像工厂那样,接收原料,加工原料和卖出产品,构造函数一开始就是接收参数,然后交由对象加工,最后return卖出产品:
function ha(name,age) { var a=new Object(); a.name=name; a.age=age; a.showa=function () { alert(this.name+" "+this.age) }; return a; //把a对象返回出去 } function hb(name,age) { //构造函数hb var b={ name:name, age:age, showb:function () { alert(this.name+" "+this.age); } }; return b; //把b对象返回出去 } ha("haha",21).showa();hb("hbhb",21).showb(); //赋值
除此之外,还有一种方法,就是构造函数模式,通过隐式的创建一个对象,在构造函数里用this来获取参数,并且不需要return,比较方便,如下所示:
function hc(name,age) { //构造函数 this.name=name; this.age=age; this.showc=function () { alert(this.name+" "+this.age) } } var hc1=new hc("hchc",21); //生成一个hc1的实例 hc1.showc();
不过,不管怎么样,上述三个方法都有一个共同的弊端,就是我们每一次生成一个实例,他如果们的属性和方法都是一样的内容,那么每次实例化它都会占用一定的内存,这样很缺乏效率和占用比较多的内存。具体的解决方法由我下一篇文章会详细的解释。
注意,在平时打代码中,不能在系统对象中随意附加方法、属性,否则会覆盖已有方法、属性。