我们在开发Web程序时或多或少都会应用到Javascript脚本程序,在当今的AJAX如此盛行的时代,JS的作用将不断的提升。这个新一代的 Web 应用程序的复杂性和交互性需要程序员以完全不同的方法来编写JavaScript 代码,我们在编写一次性的脚本显然已经不能够满足这样的需求。面向对象编程 (OOP) 是一种流行的编程方法,很多 JavaScript 库中都使用这种方法,以便更好地管理和维护基本代码。我们可以从ASP.NET AJAX库或SilverLight库以及经常见到的prototype.js库等等都采用了这种编程方式。JS支持OOP,但是又和C++,C#等非常不一样,所以这种概念一直会让人难以琢磨,这编文章将讲述 JavaScript 语言实际上如何支持面向对象编程,我们又如何应用这种方式来编写我们JS程序。
JavaScript对象
在.NET中我们知道,万物皆为对象,也就是类实例化后得到的就是对象,每个对象都有各自不同的属性和方法。JavaScript中的对象和他有所不同,我们知道在JS中我们可以通过.或[]运算符来获取或设置对象中的方法或属性,我们可以把它想象为包含键/值对的字典。下面我们来看个例子:
1
var
obj
=
new
Object();
2 obj.Now = new Date();
3 alert(obj.Now);
2 obj.Now = new Date();
3 alert(obj.Now);
我们可以用[]运算符来达到同样的效果:
1
var
obj
=
new
Object();//{};
2 obj[ " Now " ] = new Date();
3 alert(obj.Now);
2 obj[ " Now " ] = new Date();
3 alert(obj.Now);
我们还可以直接定义它的属性,效果一样:
1
var
obj
=
{"Now": new Date()}
;
2 alert(obj.Now);
2 alert(obj.Now);
以上三种方法都能够达到同样得效果,如果你用过C#3.0他的匿名赋值就有点像,也许它就是跟JS学的(只是猜测)。您不必预先声明属性Now — 如果 obj 没有该名称的属性,该属性将被直接添加到 obj。这是不是很像字典,我们可以不断地往字典里加入键/值对,我想把它想象成字典最容易理解多了。
JavaScript函数
在上面所讲述的是对象的属性,我们可以把属性值直接付给一个键(名称),其实JS的方法也是这样。JavaScript 函数实际上是具有与它关联的可执行代码的对象,我们定义方法时可以把我们的函数符给一个对象(类)中的名称(方法名称)。对于方法我想大家应该都很熟悉,这里就不多说了。
JavaScript原型
在使用 JavaScript 的面向对象编程中,原型对象是个核心概念。在 JavaScript 中对象是作为现有示例(即原型)对象的副本而创建的,该名称就来自于这一概念。此原型对象的任何属性和方法都将显示为从原型的构造函数创建的对象的属性和方法。可以说,这些对象从其原型继承了属性和方法。
在 JavaScript 中,每个函数都有名为“prototype”的属性,用于引用原型对象。此原型对象又有名为“constructor”的属性,它反过来引用函数本身。 当我们用用new创建对象时,所创建的对象将继承此函数的prototype属性。
每个 JavaScript 对象都继承一个原型链,而所有原型都终止于 Object.prototype。当您尝试访问对象的属性/方法时,JavaScript 将检查该属性/方法是否是在该对象中定义的。如果不是,则检查对象的原型。如果还不是,则检查该对象的原型的原型,如此继续,一直检查到 Object.prototype。所以我想如果用很长的prototype链对于性能方面应该会有所影响,但是现在的客户端足以承受,因此在ASP.NET AJAX中用了相当大的prototype。
JavaScript闭包
由于我对这个概念也不是特别的熟悉这个咚咚,它是JavaScript的一个高级功能,闭包是当内部函数绑定到它的外部函数的本地变量时所发生的运行时现象,这很容易让我们想到在C#中匿名的方法。我们可以通过一下实例来更好的理解这个功能:
1
var
isMoreThan300
=
filter(
2 function (x) { return (x > 300) ? true : false; } ,
3 num);
4
2 function (x) { return (x > 300) ? true : false; } ,
3 num);
4
以上事例说明了当前数num是否比300大,如果比300大我们就返回true,否则反回false。但我们可能还需要判断是否比400,500,200...等时,我们可以通过下列方法来完成:
1
function
makeMoreThan(lowerBound)
{
2 return function(numberToCheck) {
3 return (numberToCheck > lowerBound) ? true :
4
5false;
6 };
7}
8
9 function filter(pred, numberToCheck) {
10 return pred(numberToCheck);
11}
12
13
14 var isMoreThan10 = makeMoreThan( 10 );
15 var isMoreThan100 = makeMoreThan( 100 );
16 alert(filter(isMoreThan10, 1 ));
17 alert(filter(isMoreThan100, 111 ));
2 return function(numberToCheck) {
3 return (numberToCheck > lowerBound) ? true :
4
5false;
6 };
7}
8
9 function filter(pred, numberToCheck) {
10 return pred(numberToCheck);
11}
12
13
14 var isMoreThan10 = makeMoreThan( 10 );
15 var isMoreThan100 = makeMoreThan( 100 );
16 alert(filter(isMoreThan10, 1 ));
17 alert(filter(isMoreThan100, 111 ));
上面的代码运行后返回的结果是:false;true。从上面的代码可以看出makeMoreThan这个方法返回的是匿名函数,而这个匿名函数接受一个参数numberToCheck。根据作用域规则,当函数makeMoreThan退出时,变量lowerBound将失效,然而返回的匿名方法仍然带着lowerBound甚至在makeMoreThan结束后很长的一段时间内仍是如此。这样我们就可以利用这个特性来模拟.NET等中的私有变量,代码如下:
1
function
Person(name)
{
2 this.getName = function() { return name; };
3 this.setName = function(newName) { name = newName; };
4 var gender;
5 this.getGender = function() { return gender; };
6 this.setGender = function(newGender) { gender = newGender; };
2 this.getName = function() { return name; };
3 this.setName = function(newName) { name = newName; };
4 var gender;
5 this.getGender = function() { return gender; };
6 this.setGender = function(newGender) { gender = newGender; };
然后我们就可以调用Person中的方法给变量赋值,而其中的name和gender则就是我们的私有变量,当然这和.NET中所说的私有变量肯定是不同的。
Javascript继承
在OOP中继承是不可少的,在C#中要继承某个类只要用:运算符就可继承,但是在Javascript中可没有那么简单,我们还需要写点代码才能够达到此效果。通过下面的例子就能够很好的说明:
1
function
Person(name)
{
2 this.getName = function(){return name;};
3 this.setName = function(newName){name = newName;};
4}
5
6 Person.prototype.toString = function () {
7 return "My name is:" + this.getName();
8}
9
10 function XiaMenPerson(name,address) {
11 Person.call(this,address);
12 this.getAddress = function(){return address;};
13}
14
15 XiaMenPerson.prototype = new Person();//继承prototype链
16
17 XiaMenPerson.prototype.constructor = XiaMenPerson;//指定当前构造函数
18 //重写toString()方法,调用基类中的getName属性。
19 XiaMenPerson.prototype.toString = function () {
20 return "My name is:"+this.getName()+"\nCome from:"+this.getAddress();
21} ;
22
23 var p = new Person( " 网魂小兵 " );
24 alert(p);
25 // My name is:网魂小兵
26 var xp = new XiaMenPerson( " 网魂小兵 " , " 厦门 " );
27 alert(xp);
28 // My name is:网魂小兵
29 // Come from:厦门
30 alert(xp instanceof XiaMenPerson); // true;
31 alert(xp instanceof Person); // true;
32 alert(xp instanceof Object); // true;
2 this.getName = function(){return name;};
3 this.setName = function(newName){name = newName;};
4}
5
6 Person.prototype.toString = function () {
7 return "My name is:" + this.getName();
8}
9
10 function XiaMenPerson(name,address) {
11 Person.call(this,address);
12 this.getAddress = function(){return address;};
13}
14
15 XiaMenPerson.prototype = new Person();//继承prototype链
16
17 XiaMenPerson.prototype.constructor = XiaMenPerson;//指定当前构造函数
18 //重写toString()方法,调用基类中的getName属性。
19 XiaMenPerson.prototype.toString = function () {
20 return "My name is:"+this.getName()+"\nCome from:"+this.getAddress();
21} ;
22
23 var p = new Person( " 网魂小兵 " );
24 alert(p);
25 // My name is:网魂小兵
26 var xp = new XiaMenPerson( " 网魂小兵 " , " 厦门 " );
27 alert(xp);
28 // My name is:网魂小兵
29 // Come from:厦门
30 alert(xp instanceof XiaMenPerson); // true;
31 alert(xp instanceof Person); // true;
32 alert(xp instanceof Object); // true;