1.递归函数是在一个函数通过调用自身的情况下构成的。
~~~
function factorial(num){
if (num<=1){
return 1;
} else {
return num*arguments.callee(num-1);
}
}
~~~
~~~
var factorial = (function f(num){
if (num<=1){
return 1;
} else {
return num*f(num-1);
}
});
~~~
以上代码创建了一个名为f()的**命名函数表达式**,然后将它赋给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和非严格模式下都行得通。
2.闭包是指有权访问另一个函数作用域中的变量的**函数**。
3.创建闭包的常见方式,就是在一个函数内部创建另一个函数。
~~~
function createComparisionFunction(propertyName){
return function (object1, object2) {
var value1 = object1[propertyName];
var value2 = onbect2[propertyName];
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}
~~~
**当createComparisionFunction()函数返回后,其执行环境的作用域会被销毁,但他的活动对象仍然会留在内存中;直到匿名函数被销毁后,createComparisionFunction()的活动对象才会被销毁。**
~~~
//创建函数
var compareNames = createComparisionFunction('name');
//调用函数
var result = compareNames({name:'Nicolas'},{name:'Greg});
//解除对匿名函数的引用(以便释放内存)
compareNames = null;
~~~
4.匿名函数的执行环境具有全局性,因此其this对象通常指向window。但有时候由于编写闭包的方式不同,这一点可能不回那么明显。
~~~
var name = 'The window';
var object = {
name: 'My Object',
getNameFunc: function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //'The window'(在非严格模式下)
~~~
5.对已经没有用处的、闭包外部函数(保存着对象)的变量,应该将其设置为null以避免内存泄露。
~~~
function assignHandler(){
var element = document.getElementById('someElement');
var id = element.id;
element.onclick = function(){
alert(id);
};
element = null;
}
~~~
6.用作块级作用域(通常称为私有作用域)的匿名函数语法如下:
~~~
(function(){
//这里是块级作用域
})();
~~~
无论什么时候,只要临时需要一些变量,就可以使用私有作用域。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
7.私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
8.有权访问私有变量和私有函数的公有方法称为**特权方法**。有两种在对象上创建特权方法的方式。第一种是在构造函数中定义特权方法。
~~~
function MyObject(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权方法
this.publicMethod = function(){
privateVariable++;
return privateFunction();
};
}
~~~
利用私有和特权成员,可以隐藏那些不应该被直接修改的数据:
~~~
function Person(name){
this.getName = function(){
return name;
};
this.setName = function(value){
name = value;
};
}
~~~
以上代码的构造函数定义了两个特权方法:getName()和setName()。这两个方法都可以在构造函数外部使用,而且都有权访问私有变量name。但在Person构造函数外部,没有任何办法访问name。不过,在函数中定义特权方法也有一个缺点,那就是必须使用构造函数模式来达到这个目的。(方法无法复用)
9.通过在私有作用域中定义私有变量或函数,同样也可以创造特权方法,即**静态私有变量**。
~~~
(function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//构造函数
MyObject = function(){};
//公有、特权方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();
~~~
这个模式与在构造函数中定义特权方法的主要区别,就在于私有变量和函数是由实例共享的。由于特权方法是在原型上定义的,因此所有实例都是用同一个函数。而这个特权方法,作为一个闭包,总是保存着对包含作用域的引用。以这种方式创建静态私有变量会因为使用原型而增加代码复用,但每个实例都没有自己的私有变量了。
10.前面的模式是用于为自定义类型创建私有变量和特权方法的。而**模块模式**则是为单例创建私有变量和特权方法。所谓单例,指的就是只有一个实例的对象。模块模式通过为单例添加私有变量和特权方法能够使其得到增强,其语法形式如下:
~~~
var singleton = function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//特权/公有方法和属性
return {
publiceProperty: true,
publicMethod: function(){
privateVariable++;
return privateFunction();
}
};
}();
~~~
由于这个返回的对象是在匿名函数内部定义的,因此它的公有方法有权访问私有变量和函数。从本质上讲,这个对象字面量定义的是单例的公共接口。这种模式在需要对单例进行某些初始化,同时又需要维护其私有变量时是非常有用的。例如:
~~~
var application = function(){
//私有变量和函数
var conponents = new Array();
//初始化
components.push(new BaseComponent());
//公共
return {
getComponentCount: function(){
return components.length;
},
registgerComponent: function(component){
if (typeof component == 'object'){
components.push(component);
}
};
}();
~~~
在web应用程序中,经常需要使用一个单例来管理应用程序级的信息。这个简单的例子创建了一个用于管理组件的application对象。在创建这个对象的过程中,首先声明了一个私有的components数组,并向这个数组添加了一个BaseComponent的新实例。而返回对象的getComponentCount()和registerComponent()方法,都是有权访问数组components的特权方法。前者只是返回已注册的组件数目,后者用于注册新组件。
简而言之,如果必须创建一个对象并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,那么就可以使用模块模式。
11.**增强的模块模式**适合那些单例必须是某种类型的实例,同时还必须添加某些属性和(或)方法对其加以增强的情况。改写前面的例子:
~~~
var application = function(){
//私有变量和函数
var conponents = new Array();
//初始化
components.push(new BaseComponent());
//创建application的一个局部副本
var app = new BaseComponent();
//公共接口
app.getComponentCount = function(){
return components.length;
};
app.registgerComponent = function(component){
if (typeof component == 'object'){
components.push(component);
}
};
return app;
}();
~~~