JavaScript的灵活性
可以把程序写的很简单,也可以写得很复杂。这种语言也支持多种不同的编写风格,你即可以采用函数式编程风格,也可以采用更复杂一点的面向对象编程风格.
JavaScript允许您模仿其他语言的编程模式和惯用法。它也形成了自己的一些编程模式和惯用法。那些较为传统的服务器端编程语言具有的面向对象特性,JavaScript都有。
先来看一个用不同方法完成同样的任务的例子:
/* Start and stop animations using functions */
function startAnimation(){
...
}
function stopAnimation() {
...
}
这种做法很简单,但你无法创建可以保存状态并且具有一些仅对其内部状态进行操作的方法的动画对象。下面的代码定义了一个类,你可以用它创建这种对象:
/* Animation class */
var Animation = function(){
...
};
Animation.prototype.start = function(){
...
};
Animation.prototype.stop = function(){
...
};
/* usage */
var myAnim = new Animation();
myAnim.start();
...
myAnim.stop();
上述代码定义了一个名为Animation的类,并把两个方法赋给类的prototype(原型)属性。如果你更喜欢把类的定义封装在一条声明中,则可以改用下面的代码:
var Animation = function(){
...
};
Animation.prototype = {
start: function(){
...
},
stop: function(){
...
}
};
这在传统的面向对象程序员看来可能更眼熟一点,他们习惯于看到类的方法声明内嵌在类的声明之中。要是你以前用过这样的编程风格,可能想尝试一下下面的示例。
Function.prototype.method = function(name, fn) {
this.prototype[name] = fn;
}
var Animation = function(){
...
};
Animation.method('start', function(){
...
});
Animation.method('stop', function(){
...
});
Function.prototype.method用于为类添加新方法。它有二个参数。第一个是字符串,表示新方法名;第二个是用作新方法的函数。
也可以进一步修改Function.prototype.method,使其可以被链式调用。这只需要让它返回this值即可:
Function.prototype.method = function(name, fn) {
this.prototype[name] = fn;
return this;
}
var Animation = function(){
...
};
Animation.method('start', function(){
...
}).method('stop', function(){
...
});
JavaScript 弱类型语言
在JavaScript中,定义变量时不必声明其类型。介这并不意味着变量没有类型。一个变量可以属于几种类型之一,这取决于其包含的数据。JavaScript中有3种原型类型:布尔型、数值型和字符串型。此外,还有对象类型和包含可执行代码的函数类型,前者是一种复合数据类型(数组是一种特殊的对象,它包含着一批值的有序集合)。最后还有空类型(null)和未定义类型(undefined)这两种数据类型。原始数据类型按值传递,而其他数据类型则按引用传送(也就按址传递)。
与其他弱类型语言一样,JavaScript中的变量可以根据所赋的值改变类型。原始类型之间也可以进行类型转换。toString方法可以把数值或布尔值转变为字符串。parseFloat和parseInt函数可以把字符串转变为数值。双重“非”操作可以把字符串或数值转变为布尔值:
var bool = !!num;
弱类型的变量带来了极大的灵活性。因为JavaScript会根据需要进行类型转换,所以一般来说,你不用为类型错误操心。
函数是一等对象
在JavaScript中,函数是一等对象,它们可以存储在变量中,也可以作为参数传给其他函数,可以作为返回值从其他函数传出,还可以在运行时进行构造。在与函数打交道时,这引起特性带来了极大的灵活性和极强的表达能力。
可以用function(){...}这样的语法创建匿名函数。它们没有函数名,但可以被赋给变量。下面就是一个匿名函数的救命:
(function(){
var iVal = 10;
var iBar = 3;
alert( iVal * iBar ); // 30
})();
上面这个函数在定义后便立即执行,甚至不用赋给一个变量,出现在函数声明之后的一对括号立即对函数进行了调用。括号中空无一物,但也并不是非得如此:
(function(iVal, iBar){
alert( iVal * iBar ); // 30
})(10, 3);
这个匿名函数与前一个等价,只不过变量没有在函数内部用var声明,而是作为参数从外部传入而已。这个函数也可以返回一个值。这个返回值可以被赋给一个变量:
var barz = (function(iVal, iBar){
return iVal * iBar;
})(10, 3);
匿名函数最有趣的用途是用来创建闭包。闭包(closure)是一个受到保护的变量空间,由内嵌函数生成。JavaScript具有函数级的作用域。这意味着定义在函数内部的变量在函数外部不能被访问的。JavaScript的作用域又是记法性质的。这意味着函数运行在定义它的作用域中,而不是调用它的作用域中。把这二个因素结合起来,就能通过把变量包裹在匿名函数中而对其加以保护。你可以这样创建类的私用(private)变量:
var baz;
(function(){
var iVal = 10;
var iBar = 3;
baz = function(){
return iVal * iBar ;
}
})();
baz();
变量iVal 和 iBar 定义在匿名函数中,因为函数baz定义在这个闭包中,所以它能访问这二个变量,即使是在该闭包执行结束后。
对象的易变性
在JavaScript中,一切都是对象(除了那三种原始数据类型。即便是这些类型,在必要的时候也会被自动包装为对象),而且所有对象都是易变的。这意味着你能使用一些在大多数别的语言中不允许的技术.
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototye = {
getName: function(){
return this.name;
},
getAge: function(){
return this.age;
}
}
var lili = new Person('lili', 28);
var nana = new Person('nana', 24);
Person.prototype.getGreeting = function(){
return 'Hi ' + this.getName() + "!";
}
// static method
lili.displayGreeting = function(){
alert(this.getGreeting());
}
在这个例子中,类的getGreeting方法是在已经创建了类的两个实例之后才添加的,但这两个实例仍然能获得这个方法,其原因在于prototype对象的工作机制,对象lili还得到了displayGreeting方法,而别的实现却没有。
与对象的易变性相关的还有内省(introspection)的概念,你可以在运行时检查对象所具有的属性和方法,还可以使用这种信息动态实例化类和执行其方法(这种技术称为反射(reflection)),甚至不需要在开发时知道它们的名称,这些技术在动态脚本编程中发挥着重要作用,而静态语言(例如C++)则缺乏这样的特性。
JavaScript中设计模式特点:
1> 可维护性。设计模式有助于降低模块间的耦合程序。这使对代码进行重构和换用不同的模块 变得更容易,也使程序在大型团队中的工作以及与其他程序的合作变得列容易。
2>沟通。设计模式为处理不同类型的对象提供了一套通用的术语。程序员因此可以更简单地描述自己的系统的工作 方式。你不用进行冗长的说明,往往这样一句话就足够了:“它使用了工厂模式”。第一个模式都有自己的名称,这意味着你可以在层面上对其进行讨论,而不必涉足过多的细节。
3>性能。设计模式可以大幅提高程序的运行速度,并减少需要传送到客户端的代码量。这方面最重要的例子是享元模式和代码模式。
如下二个理由而不使用设计模式:
1>复杂性,获得可维护性往往要付出代码,那就是代码可能会变得更加复杂、更难被程序设计新手理解。
2>性能。尽管某些模式能提升性能,但多数模式对代码的性能都有所拖累。这种拖累可能微不足道,也可能完全不能接受,这取决于项目的具体需要。
实现设计模式比较容易,而懂得应该在什么时候使用什么模式则较为困难。未搞懂设计模式的用途就盲目套用,是一种不安全的做法。你应该尽量保证所选用的模式就是最恰当的那种,并且不要过度牺牲性能。