JavaScript中创建函数主要有两种方法:函数声明和函数表达式。这两种方式都有不同的适用场景。这里主要介绍函数表达式的应用场景。
1.函数递归:是在一个函数通过调用名字调用自身的情况下构成的
看下面的例子:
1
2
3
4
5
6
7
|
function factorial(num){ if (num <= 1){ return 1; } else { return num * factorial(num - 1); } } |
这是一个经典的阶乘函数,但是这个例子存在的一个问题是函数名称factorial与函数体紧密耦合在一起,执行下面的语句就会报错:
1
2
3
|
var anotherFactorial = factorial; factorial = null ; console.log(anotherFactorial(5)); //"Uncaught TypeError: factorial is not a function" |
报错的原因在于在函数体内部会调用factorial函数,而变量factorial对函数的引用已经被解除所以报错。这种情况的解决方法一般可以使用arguments.callee来解决,arguments.callee始终指向当前的函数,例如:
1
2
3
4
5
6
7
|
function factorial(num){ if (num <= 1){ return 1; } else { return num * arguments.callee(num - 1); } } |
这样在此执行anotherFactorial函数就可以得到正确结果了。但是在严格模式"strict"下,arguments.callee是不能通过脚本访问的,这是就可以使用函数表达式来解决这个问题了,例如:
1
2
3
4
5
6
7
8
|
var factorial = ( function f(num){ if (num <= 1){ return 1; } else { return num * f(num - 1); } }); console.log(factorial(5)); //"120" |
2.闭包:是指有权访问另一个函数作用域中的变量的函数
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
闭包耗费内存的原因:闭包作用域链引用了外部函数的变量对象,闭包没有执行完毕,作用域链引用的外部函数的变量对象都不会被回收,闭包中不需要使用的变量尽早解除引用
闭包可以用在许多地方。它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
闭包在访问外部变量、this、引用HTML元素的时候需要注意:
1.闭包只能取得包含函数中任何变量的最后一个值
2.如果闭包的外部环境是一个函数,那么闭包中的this事window,如果闭包的外部环境是object,那么this是这个对象
3.闭包在引用HTML元素时,一旦HTML元素被引用,那么该元素将无法被销毁(内存泄露)
闭包的应用场景:自执行函数、私有属性和方法
3.模仿块级作用域:JavaScript中是没有块级作用域概念的。也就是说,在块级语句中定义的变量,实际上是在包含函数中(外部函数)而非语句中创建的。
1
2
3
4
5
6
|
function outputNumber(count){ for ( var i=0;i<1000;i++){ alert(i); } alert(i); //count } |
该函数在java、C#等语言中,变量i只会在for循环语句中有定义,循环结束,i也就被销毁了。但在JavaScript中,变量i是定义在outputNumber()活动对象中的,因此在它定义开始,就可以在函数内部访问它。即使重新声明同一个变量,也不会改变它的值。
匿名函数可以用来模仿块级作用域并避免这个问题,用作块级作用域(也称私有作用域)的匿名函数的语法如下:
1
2
3
|
( function (){ //这是块级作用域 })() |
以上代码定义变调用了一个匿名函数,将函数声明包含在一个小括号里面,表示它是个函数表达式。紧跟其后的另一对小括号会立即调用这个函数。
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
原文:http://www.cnblogs.com/crbluesky/p/7652312.html