>JavaScript如何面向对象编程
众所周知,JavaScript并没有类的概念,只有对象的概念,即万物皆对象。
因此js的OOP方法跟我们熟悉的JAVA语言等完全不同。我们能做到的,只能是利用JS语言的某些机制,去模拟一个类,并对此做继承和接口。
前面部分将一步步讲清楚是怎样来的,具体完成的例子,可以直接滑动到最后面看实例。
转载请注明来自:[CSDN]身披白袍's博客
>重要的prototype
prototype是js中一个重要的属性,很多地方直接翻译成原型。其实也没多大问题,就是理解上不是太方便。我更倾向于将它翻译成”公共区“。
在js语言中,每一个函数对象都会默认添加一个prototype的属性,根据js万物皆对象的思想,这个prototype也是一个对象。
不那么严谨的说,这个prototype是这一个函数的“公共区”,也就是这个prototype里有的,这个函数都有。
现在已知:
func.prototype.attr = function() | value ;
这样的写法,是向函数func的这个“公共区”添加了名字为attr的属性(对象)。注意到这个attr根据js万物皆对象的实现,可以是一个函数或者一个值,那么我们就可以根据这个性质来模拟OOP中的类。
>一个类需要有哪些因素?
- 其一,这个类某个对象自身的属性;
- 其二,这个类所有对象都要有的方法;
>如何定义一个类
我们利用js中的函数来模拟定义一个类,这个类有三种属性:私有属性name、两个公共属性type、静态属性age;有一个方法eat();
function Animal(str){
var name;//私有属性
this.type = str;//公共属性
this.sex = "male";//公共属性,必须初始化
}
Animal.prototype.age = 7;//类的静态属性
Animal.prototype.eat = function(){//添加一个类的方法
console.log("动物吃东西");
}
观察到,我们完成静态属性时,完全参照了上面prototype的特性,把一个属性添加到公共区,以作为这整个类(用函数模拟类)的静态属性。
>如何继承一个类?(最常用的组合继承法)
请回忆一下我们在JAVA中,我们继承一个类,主要是继承了什么呢?
- 父类的属性;
- 父类的方法;
首先,我们继承一个类的属性。
假如现在我们有一个类:Dog,是Animal的子类,我们可以这样继承他的两个公共属性,并同时完成父类的构造:
function Dog(cStr){
Animal.call(this,cStr);//继承父类属性
var pri;//子类的私有属性
this.eyeColor = "red"//子类的公共属性
}
重点就在于Animal.call(this,cStr),call函数有官方的理解,但我个人更偏向于一种更好的理解方式:
Animal类,用子类(this此处指代Dog)和其属性cStr构造父类,并将父类的属性复制一份到子类(this,此处指代子类Dog)中,从而实现了模拟类的属性继承。
接着,我们要进行类方法的继承。
还记得类方法都存在哪里吗?在父类的prototype里。因此,我们就要像复制父类属性一样,把父类的prototype复制一份,赋值给子类的prototype:
Dog.prototyte = Object.create(Animal.prototyte);//继承父类的方法
Object.create(obj)方法是js中,将obj对象(别忘了万物皆对象,prototype也是对象)复制一份的方法。把父类的prototype复制一份,赋值给子类,就等同子类继承了父类的所有方法。
但是,js中,每个对象都隐含一个构造方法,且这个隐含的构造方法都默认存在“公共区”prototype中(在Java中构造方法也是类共有的)。
我们把父类的prototype直接复制给子类,就把子类的构造方法给洗掉了,因此我们还需要在继承父类方法的代码下面,手动把子类的构造方法给上:
Dog.prototype.constructor = Dog;//重定向子类的构造函数
>如何重写一个方法?
假如Dog类要重写父类Animal的父类方法eat,那么只需要在继承父类方法和重定向构造函数后,往“公共区”直接重写就好了:
Dog.prototype.eat = function(){
console.log("狗吃东西");
}
转载请注明来自:[CSDN]身披白袍's博客
>如何定义接口?
接口的定义详细可见:http://www.cnblogs.com/xhb-bky-blog/p/5887242.html
但是我们要清楚,js是一种脚本语言,在真正执行前,根本不会有IDE提示你哪个接口实现了哪个接口没实现。
而接口最大的作用是提示程序员,给程序员一个统一的编写规范,本质上,与注解无差。因此我们完全可以显示地利用注释和字符串告诉编程者自己这个类到底要实现哪些类:
/*注释某个接口的所有内容
interface ActionInterface{
void swim();
}
*/
function Dog(){
this.Interface = ["ActionInterface"];//显示标注这个类要实现的接口
}
//实现这个接口
Dog.prototype.swim = function(){
console.log("狗狗游泳");
}
对于大部分js脚本,这样就够了,太多的检测只是徒增烦恼和复杂度。
>一个测试实例见此
以上都是理论,比较散。建议到下面这个git去看一个测试实例:
https://gitee.com/shenpibaipao/JavascriptDeOOPBianChengCeShiLiZi