
6.3 类和抽象
很多语言都有类和抽象,1967年挪威计算中心的Kisten Nygaard和Ole-Johan Dahl开发了Simula 67语言,它提供了比子程序更高一级的抽象和封装,引入了数据抽象和类的概念,被认为是第一个面向对象语言。
6.3.1 基于对象(Object-Based)和面向对象(Object-Oriented)
基于对象和面向对象是两个极易混淆的概念。基于对象(Object-Based)通常指的是对数据的封装,以及提供一组方法对封装过的数据操作,比如C的IO库中的FILE *就可以看成是基于对象的。面向对象(Object Oriented, OO),用纯粹的理论去理解就是必须具备封装、继承、多态三大特点,缺一不可。
程序=基于对象操作的算法+以对象为最小单位的数据结构。对代码的封装使得代码得以复用,减少了代码的体积,同样使问题简化。合理的数据结构使得数据量减少,或者简化数据操作算法。
Java是面向对象的程序设计语言,JavaScript很像Java但并不支持所有Java面向对象的功能,只能支持其中一部分。
在JavaScript里,你所知的所有东西几乎都是对象,但是它的语法里并没有class (类),所以我们只能把它理解为基于对象。VB(非VB.net)、Flash ActionScript 2.0和VBScript等都和JavaScript一样是基于对象的。
面向对象和基于对象之间的界限既清楚又模糊。说它清楚,是因为面向对象语言必须从语法上直接支持继承和动态绑定,而基于对象语言无法从语法上直接做到这一点。说它模糊,是因为基于对象的语言可以在没有语法直接支持的情况下,通过一些技巧达成与面向对象语言相同的效果。
6.3.2 用JavaScript创建一个类
JavaScript虽然不能直接从语法上使用class,但是能够模拟出“类”的效果。Stoyan Stefanov(Yahoo YSLOW项目的架构师)编写的Object-Oriented JavaScript这本书介绍了很多种面向对象的编程方法。在这里介绍一种最容易理解的构造函数法。
这是经典方法,在很多JavaScript代码中都可以见到。它用构造函数模拟“类”,在其内部用this关键字指代实例对象。
function book(){ this.name = "JavaScript 实例大全" }
生成实例的时候,使用new关键字。
var mybook = new book(); alert(mybook.name); //JavaScript 实例大全
如果不想每次都用new关键字,还可以这样改造构造函数。
function book(){ if (! (this instanceof book)) { return new book() }; this.name = "JavaScript 实例大全" }
这时候,就可以省去等号后面的new关键字。instanceof操作符专门用来检测对象是否是某个类的实例,如果不是就自动返回一个new过的对象。
6.3.3 静态属性、方法和动态属性、方法
属性和方法是类最基本的组成,动态方法和属性必须要实例化后才能访问,而静态方法和属性无须实例化就可以使用,这是类的基础功能。
对于6.3.2小节中的代码,没有实例化book时,是无法访问到book.name这个属性的,也就是说this挂载的都是动态属性和方法。如何才能设置静态属性和方法呢?
book.material = "纸质"; book.getSize = function(type){ switch(type){ case 16: return "16K" case 32: return "32K" default: return "16K" } };
一般大家看的书都是纸质书,所以book.material就是一个静态属性,无须实例化。另外,book.getSize()是一个静态方法,书一般分16开或32开,通过传递16或32的值即可返回一个开数值,默认是16开。
说了静态成员,再介绍一下动态属性和方法的挂载方法,那就是通过prototype属性,这对于不想用this或不能用this的情况是一个很好的补充。
book.prototype.pages = 460; book.prototype.randomInfo = function(){ var l = ["JavaScript 基础应用" ,"JavaScript 与HTML5 表单应用" ,"JavaScript 与HTML5 高级应用" ,"JavaScript 与jQuery 综合应用" ,"JavaScript 与Node.js 综合应用"]; return l[(Math.random()*l.length)>>0] };
调用时忽略prototype,直接是实例名.属性和实例名.方法,如mybook.pages和mybook.randomInfo()。书页和书的目录都是不同的,它们应该是动态变化的。
6.3.4 JavaScript继承
假如笔者写了很多书,但是笔者的基本信息是不变的,所以每本书的作者信息相同。用prototype也可以实现继承,【范例6-2】整合前面的代码后又加入了继承。
【范例6-2 JavaScript继承】
1. function z3fBook(){}; //父类 2. z3fBook.prototype.author ={ 3. name:’张三封’ 4. , QQ:'10590986' 5. , web:'z3f.me' 6. } 7. function book(){ 8. if (! (this instanceof book)) { return new book() }; 9. this.name = "JavaScript 实例大全" 10. } 11. book.prototype = new z3fBook(); //继承父类信息 12. book.material = "纸质"; 13. book.getSize = function(type){ 14. switch(type){ 15. case 16: 16. return "16K" 17. case 32: 18. return "32K" 19. default: 20. return "16K" 21. } 22. }; 23. book.prototype.pages = 460; 24. book.prototype.randomInfo = function(){ 25. var l = ["JavaScript 基础应用" 26. , "JavaScript 与HTML5 表单应用" 27. , "JavaScript 与HTML5 高级应用" 28. , "JavaScript 与jQuery 综合应用" 30. , "JavaScript 与Node.js 综合应用"]; 31. return l[(Math.random()*l.length)>>0] 32. }; 33. var yourBook = book(); 34. alert(yourBook.author.name); //张三封 35. alert(yourBook.author.QQ); //10590986 36. alert(yourBook.pages); //460
从以上代码可以看到,z3fBook()作为一个基类,保存了作者的基本信息。作者编写的每一本书book都应该继承该信息。在读者拿到每一本具体的书时就相当于yourBook实例化后的book对象,所以在输出yourBook.author.name这些只属于基类提供的信息时也能正确取值,表明继承成功。
6.3.5 私有属性和方法
只要不挂在this和prototype上的属性和方法都是私有的,把【范例6-2】中的book构造函数改造如下:
function book(){ if (! (this instanceof book)) { return new book() }; var bookname = "JavaScript 实例大全" this.getName = function(){ return bookname; } this.setName = function(name){ bookname = name; } }
如果不提供getName()和setName()方法,bookname就只能在内部用。外部要获取和操作只能通过公共的方法:
alert(yourBook.getName()); //JavaScript 实例大全
6.3.6 抽象
抽象是从众多的事物中抽取出共同的、本质性的特征,而舍弃非本质的特征。例如,苹果、香蕉、梨、葡萄、桃子等,它们共同的特性就是水果,得出水果概念的过程就是一个抽象的过程。
要抽象,就必须进行比较,没有比较就无法找到在本质上共同的部分。共同特征是指那些能把一类事物与他类事物区分开的特征,这些具有区分作用的特征又称本质特征。因此抽取事物的共同特征就是抽取事物的本质特征,舍弃非本质的特征。所以抽象的过程也是一个裁剪的过程。
在抽象时,同与不同,决定于从什么角度来抽象。抽象的角度取决于分析问题的不同目的。
抽象是一种逻辑思维活动过程,正确的和具有实用性的概念应该是简洁和清晰的,否则它不但不能成为我们进行思维的利器,反而会成为思维的包袱。所以在抽象思维的时候应当了解各种基础概念,似是而非只会给程序留下漏洞。
抽象出来的特征通常可以分成3类:
● 相同
● 相似,如move、moveTo
● 相反,如open和close、show和hide