常见的函数表达式创建函数形式。
var functionName = function(arg0, arg1, arg2){
//函数体
};
这种形式看起来好像是常规的变量赋值语句,即创建一个函数并将它赋值给变量 functionName。这种情况下创建的函数叫做匿名函数(anonymous function),因为 function 关键字后面没有标识符。(匿名函数有时候也叫拉姆达函数。)匿名函数的 name 属性是空字符串。
7.1 递归
递归函数是在一个函数通过名字调用自身的情况下构成的,
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
arguments.callee 代替函数名
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
7.2 闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
闭包的创建
创建闭包的常见方式,就是在一个函数内部创建另一个函数,并且内部函数引用外部函数的变量。
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
7.2.1 闭包与变量
作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最 后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
上述例子中,内部函数保存的外部函数整个变量对象,而外部的变量对象中的i只有一个,循环结束后,i为10;所以最终内部函数返回的i是最终循环后的10。
7.2.2 关于this对象
this 对象是在运行时基于函数的执行环境绑定的2
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"
上述例子中,object.getNameFunc()调用后是返回了一个函数,我们假设返回的函数叫fn,那么object.getNameFunc()()相当于fn(),也就是相当于在全局环境调用返回的这个函数,所以this是指向window的。
7.2.3 内存泄漏
如果闭包的作用域链中保存着一个HTML 元素,那么就意味着该元素将无法被销毁。
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){
alert(element.id);
};
}
以上代码创建了一个作为 element 元素事件处理程序的闭包,而这个闭包则又创建了一个循环引 用(事件将在第 13 章讨论)。由于匿名函数保存了一个对 assignHandler()的活动对象的引用,因此 就会导致无法减少 element 的引用数。只要匿名函数存在,element 的引用数至少也是 1,因此它所 占用的内存就永远不会被回收。因此我们要手动把元素赋值为null。
function assignHandler(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
alert(id);
};
element = null;
}
必须要记住:闭包会引用包含函数的整个活动对象,而其中包含着 element。即使闭包不直接引用 element,包含函数的活动对象中也仍然会保存一个引用。因此,有必要把 element 变量设置为 null。
7.3 模仿块级作用域
截至到ES5,Javascript没有块级作用域的概念。我们可以用IIFE来模仿一个块级作用域(通常称为私有作用域)
(function(){
//这里是块级作用域
})();
这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
7.4 私有变量
任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。私有变量包括函数的参数、局部变量和在函数内部定义的其他函数。
function add(num1, num2){
var sum = num1 + num2;
return sum;
}
在这个函数内部,有 3 个私有变量:num1、num2 和 sum。
如果在这个函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。而利用这一点,就可以创建用于访问私有变量的公有方法。我们把有权访问私有变量和私有函数的公有方法称为特权方法(privileged method)。
7.4.1 静态私有变量
通过在私有作用域中定义私有变量或函数,同样也可以创建特权方法,其基本模式如下所示。
(function(){
//私有变量和私有函数
var privateVariable = 10;
function privateFunction(){
return false;
}
//构造函数
MyObject = function(){};
//公有/特权方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();
7.4.2 模块模式
所谓的模块模式(module pattern)则是为单例创建私有变量和特权方法。所谓单例(singleton),指的就是只有一个实例的对象。按照惯例,JavaScript 是以对象字面量的方式来创建单例对象的。
var singleton = {
name : value,
method : function () {
//这里是方法的代码
}
};