1. 基于对象还是面向对象?
面向对象技术是现代软件开发中的重要技术之一。面向对象变成的好处毋庸置疑,现在的主流语言如Java、C++都是面向对象的。现在的面向对象理论更多的是使用Java或C++进行描述,究其根源,在于这些语言都是传统的面向对象语言,具有面向对象理论所指明的一切特性:类、封装、继承、多态等等。
相比而言,一些动态语言如JavaSript就显得不那么面向对象——至少,在JavaScript中并没有类class这一关键字。但是,在JavaScript中并不是没有类的概念。于是有人说,JavaScript是基于对象的语言,而不是面向对象的语言。
面向对象的语言具有三个特性:封装、继承和多态,三者缺一不可;基于对象的语言通常仅仅是使用对象,其实现的是封装,并没有提供后两种特性。的确,从语法上来说,JavaScript并没有特定的语法或者在语言级别上来实现继承和多态。但是,这并不妨碍我们使用这些特性。这是因为,JavaScript是一种灵活的语言,它是相当的灵活,以至于这些并没有提供的东西,更确切的说,是没有明确的表明的东西,都是可以实现和使用的!那么,你还能说JavaScript是基于对象而不是面向对象的吗?
面向对象也是一种思想,任何语言,包括C语言,同样可以使用面向对象的思想去解决现实生活中的各种问题。到底是基于对象还是面向对象,这些概念让计算机哲学家门去争论吧——相信他们的争论最终也会和先有鸡还是先有蛋的问题一样的结果——我们所要做的,是要使用这种语言提供的机制去解决我们的问题。
2. 为什么要有JavaScript的面向对象编程?
这个问题很严肃——这取决你问题的规模和应用的范围。就像JavaEE和PHP一样:PHP能实现的东西,JavaEE都能实现,那么,为什么还要有PHP?因为JavaEE太复杂了,对于一些简单的系统,根本没有必要是使用它,也就是所谓的“杀鸡焉用牛刀”。
JavaScript主要应用于Web开发中。在传统的Web开发模式中,JavaScript起到的是一些点缀的作用,只完成很有限的功能,例如表单验证等。于是,JavaScript多被当做一种过程性语言使用,很难完成复杂的功能。而今天Web2.0的时代,Ajax大行其道,很多复杂的脚本成为其必须的组成部分。在Ajax应用中利用JavaScript面向对象编程风格,能够使逻辑更加清晰,也更有利于问题的解决。
如果你想用JavaScript编写一个库,比如ExtJS或者YUI,很难想象你的类库不使用面向对象的编程风格——否则的话,无论是对你还是对使用者的智力都将是一个前所未有的考验!或许,自从面向对象思想提出之后,已经很难有类库不使用面向对象的方式实现了,即便是C语言的库诸如gtk+,也是用C语言将面向对象的思想表现的天衣无缝。面向对象的思想对于大型程序的编写和使用具有不可替代的作用。
本系列文章将试图向读者阐述JavaScript的面向对象程序设计。尽管JavaScript中具有很多浏览器相关的概念,如document等内置对象,但是本系列将不涉及这些问题,并且将假设读者已经有JavaScript基础的语法知识等。本系列文章不会从头开始讲述JavaScript的语法,仅仅从纯粹的面向对象角度审视JavaScript,或许,你将会看到一个教程:面向对象程序设计——JavaScript语言描述。这才是本系列文章的目的。
- var objAyyar = []; // 1
- var objAyyar = [2]; // 2
- var objAyyar = ["a", "b", "c"]; // 3
- var objAyyar = [new Date(), 123, "abc"]; // 4
- int[] array = new int[10];
- var arr = [1, 2, 3, 4, 5];
- alert(arr.length); // 数组长度为5
- alert(arr[3]); // arr[3] = 4
- arr[9] = 10; // 改变了数组的长度为10
- alert(arr[7]);
- alert(arr.length);
- var arr = [1, 2, 3, 4, 5];
- alert(arr.length); // 数组长度为5
- delete arr[3]; // 删掉第4个元素
- alert(arr.length); // 长度不变
- alert(arr[3]); // arr[3] = undefined
- arr.length = 4; // 缩短长度
- alert(arr[4]);
- arr.length = 10; // 增加长度
- alert(arr[6]);
- var arr = [1, 2, 3];
- alert(arr[1] == arr["1"]);
- arr["js"] = 4;
- alert(arr["js"]);
- alert(1 == "1"); // true
- alert(1 === "1"); // false
- public class Person {
- private String name;
- private int age;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public void introduction() {
- System.out.println("My name is " + this.name + ", my age is " + this.age);
- }
- public static void main(String[] args) {
- Person p = new Person();
- p.setName("Tom");
- p.setAge(20);
- p.introduction();
- }
- }
- var Person = {
- "name": "Tom",
- "age": 20,
- "introduction": function() {
- alert("My name is " + this.name + ", my age is " + this.age);
- }
- };
- Person.introduction();
- var Person = {
- name: "Tom",
- age: 20,
- introduction: function() {
- alert("My name is " + this.name + ", my age is " + this.age);
- }
- }
- Person.introduction();
- alert(Person.name); // Tom
- // alert(Person."name");
- alert(Person["name"]); // Tom
- alert(Person[name]); // undefined
- var Person = {}; // 创建一个空对象
- Person.name = "Tom"; // 添加一个属性name,并赋值为Tom
- Person["age"] = 20; // 用另外的办法新增属性
- Person.introduction = function () {
- alert("My name is " + this.name + ", my age is " + this.age);
- };
- Person.introduction();
- for(var field in Person) { // 使用foreach循环列出对象中所有属性
- alert("field name: " + field + "; value: " + Person[field]);
- }
- delete Person.name; // 删除name属性
- Person.introduction();
- alert(name in Person); // 使用in操作符判断属性是否存在
- var date = new Date();
- alert(date.constructor);
- alert(date.constructor == "Date"); // false
- alert(date.constructor == Date); // true
在很多语言中,函数(Java里面成为方法)和对象时截然不同的两种东西。函数被定义为对象的动作,或者是全局的(像在C++中的main函数一样)。但是在JavaScript中,函数和对象的界限却显得不那么明显。
1. 函数的定义
JavaScript中有很多种定义函数的方法:
- function hello() { alert("Hello!"); }
- var hello1 = function() { alert("Hello!"); };
- var hello2 = new Function("", "alert('Hello!');");
- hello();
- hello1();
- hello2();
上面给出了三种JavaScript的函数定义语句。第一句是常见的定义,看上去和Java等语言没有太大的不同。这句是定义了一个具名函数,按照上面的例子,这里的函数定义名字为hello。第二句是将一个匿名函数定义好后赋值给一个变量,于是通过这个变量就可以引用这个匿名函数。这两句看上去效果差不多,但是它们是不一样的:第一句定义的是一个具名函数,第二句定义的是一个匿名函数——尽管你可以通过这个变量引用到这个匿名函数,但实际上它还是匿名的。它们的区别可以由下面的看出:
- hello();
- hello1(); // error
- function hello() { alert("Hello!"); }
- var hello1 = function() { alert("Hello!"); };
具名函数的作用范围是全局的:你可以在定义之前使用这个函数。但是匿名函数的定义是后向的,像C/C++一样,必须在定义之后才能使用。这就是为什么hello可以使用,但是hello1就会有错误。然后试想一下这是为什么呢?JavaScript的解释过程和HTML一样是从上到下的。所以,这里的匿名函数就相当于是一个变量的定义,因此在JavaScript解释器解释执行时并不知道这个变量的定义,因此发生错误。但是,对于函数的定义则是扫描全局。
第三个语句就很有意思了。它创建了一个Function类的对象。这个构造函数(姑且这么叫吧)具有两个参数,第一个是函数的参数,第二个是函数体。具体来说,下面的两个函数定义是等价的:
- function sayHelloTo(name) {
- alert("Hello, " + name);
- }
- var sayHelloTo1 = new Function("name", "alert('Hello, ' + name)");
这种使用Function进行定义的方式并不常见,但是这个语句显示的特性却很有趣:它意味着,你可以使用这种构造函数在运行时动态的构造函数!这是一般的语言没有的特性。
2. 函数的参数
JavaScript的函数也是相当的灵活,不仅是它的定义方式多种多样,甚至它的参数都有“奇怪”的行为。由于JavaScript是弱类型的语言,因此,它不能对你的函数参数类型做检测,甚至不能保证你传入的参数个数是否和函数定义一致。这就需要有一些特殊的检测。
- function sum2(a, b) {
- alert(a + b);
- }
- sum2(1); // NaN
- sum2(1, 2); // 3
- sum2(1, 3, 5); // 4
看这个例子,仅仅接受两个参数的函数,在调用时可以有任意个参数!但是,它仅取用符合条件的个数,在这里也就是前两个参数。所以,当你传入一个参数时,JavaScript试图将两个数字加起来,结果第二个参数不存在,因此返回值是NaN。第三种情况,实参个数多于形参个数,此时JavaScript只取前两个参数相加。
尽管很不正式,但是可以说,JavaScript的函数参数是不定参数,也就是说,你可以传入任意的参数值。使用JavaScript函数内置的arguments就可以遍历所有传入的参数。比如下面的代码:
- function sum() {
- var total = 0;
- for(var i = 0; i < arguments.length; i++) {
- total += arguments[i];
- }
- alert(total);
- }
- sum(1, 2);
- sum(1, 2, 3);
arguments的行为很像数组,但它并不是数组。可以使用typeof操作符看一下,也可以调用它的constructor属性。
这里有一点需要说明,arguments有个callee属性,可以调用arguments自身所在的函数。也就是说,可以通过这个属性递归调用函数自身。所以,即使是匿名函数,也可以实现递归调用。如:
- function sum(n) {
- if(n <= 1) {
- return 1;
- }
- return n + arguments.callee(n - 1); // 递归调用自身
- }
- alert(sum(100));
我觉得大家都会知道这个著名问题的答案的。
3. 函数也是对象
回想一下上面的第三个语句,它已经强烈暗示了,函数其实也是对象!那么,作为一个对象,函数应该具有对象的一切特性:添加属性、删除属性、作为返回值等等。是的!JavaScript的函数就是这么样的!
- function hello() {
- alert("Hello!");
- }
- hello.name = "Tom"; // 添加属性
- alert(hello["name"]);
- delete hello.name; // 删除属性
- alert(hello.name);
- // 赋值给变量
- var hello1 = function() { alert("hello1"); };
- hello1();
- // 作为数组元素
- function show(x) { alert(x); }
- var arr = [show];
- arr[0](5);
- // 作为函数的参数
- function callFunc(func) {
- func();
- }
- callFunc(function() {
- alert("Inner Function.");
- });
- // 作为函数的返回值
- function show() {
- return function(n) {
- alert("number is " + n);
- };
- }
- show()(10);
瞧!凡是对象可以做到的,函数统统都能做到!JavaScript中的函数就是对象!
现在我们已经从数组,逐渐开始到对象和函数。这些都是基本概念,后面,我们将对JavaScript的面向对象特性做进一步的介绍。