JavaScript中建立函数主要有两种方法:函数声明和函数表达式。这两种方式都有不一样的适用场景。这里主要介绍函数表达式的应用场景。javascript
一.函数递归:是在一个函数经过调用名字调用自身的状况下构成的html
看下面的例子:java
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"
|
二.闭包:是指有权访问另外一个函数做用域中的变量的函数。建立闭包的常见方式,就是在一个函数内部建立另外一个函数 。this
做用域链的这种配置机制引出了一个值得注意的反作用,即闭包只能取得包含函数中任何变量的最后一个值。别忘了闭包所保存的是整个变量对象,而不是某个特殊的变量。下面这个例子能够清晰地说明这个问题。spa
这个函数会返回一个函数数组。表面上看,彷佛每一个函数都应该返本身的索引值,即位置 0 的函数返回 0,位置 1 的函数返回 1,以此类推。但实际上,每一个函数都返回 10。由于每一个函数的做用域链中都 保 存 着 createFunctions() 函 数 的 活 动 对 象 , 所 以 它 们 引 用 的 都 是 同 一 个 变 量 i 。 当createFunctions()函数返回后,变量 i 的值是 10,此时每一个函数都引用着保存变量 i 的同一个变量对象,因此在每一个函数内部 i 的值都是 10。可是,咱们能够经过建立另外一个匿名函数强制让闭包的行为符合预期,以下所示。.net
在重写了前面的 createFunctions()函数后,每一个函数就会返回各自不一样的索引值了。在这个版本中,咱们没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将当即执行该匿名函数的结果赋给数组。这里的匿名函数有一个参数 num,也就是最终的函数要返回的值。在调用每一个匿名函数时,咱们传入了变量 i。因为函数参数是按值传递的,因此就会将变量 i 的当前值复制给参数 num。而在这个匿名函数内部,又建立并返回了一个访问 num 的闭包。这样一来, result 数组中的每一个函数都有本身num 变量的一个副本,所以就能够返回各自不一样的数值了。code
闭包能够用在许多地方。它的最大用处有两个,一个是能够读取函数内部的变量,另外一个就是让这些变量的值始终保持在内存中。
闭包在访问外部变量、this、引用HTML元素的时候须要注意:
1.闭包只能取得包含函数中任何变量的最后一个值
2.若是闭包的外部环境是一个函数,那么闭包中的this是window,若是闭包的外部环境是object,那么this是这个对象
3.闭包在引用HTML元素时,一旦HTML元素被引用,那么该元素将没法被销毁(内存泄露)
闭包的应用场景:自执行函数、私有属性和方法以及采用函数引用方式的setTimeout调用
三.模仿块级做用域: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