函数表达式的特征
1、函数表达式和函数声明:
函数声明:首先是function关键字然后才是函数名,函数声明有函数提升,这就意味着可以将函数调用写在函数声明之前;
函数表达式:首先是函数名然后才是function关键字,这种方式类似于变量的赋值,将一个匿名函数赋给一个函数名变量;
2、使用函数实现递归;
递归函数是在一个函数通过名字调用自身的情况下构成的;
下面看一个经典递归函数:
function test(num){
if(num<=1){
return 1;
}else{
return num*test(num-1);//调用自身
}
}
var foo=test;
test=null;
alert(foo(4));//这里会报错,这是因为test将函数引用赋给了foo,然后test赋值为null切断了函数引用,foo自然也无法获取到函数引用。
在这种情况下使用arguments.callee可以解决这个问题:arguments.callee是一个指针,指向正在执行的函数,因此可以用来实现递归。
function test(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1);//调用自身
}
}
var foo=test;
test=null;
alert(foo(4));//这里不会报错
3、闭包
闭包:就是有权访问另一个函数作用域中的变量的函数。在一个函数内部创建另一个函数就可以实现闭包
如何创建作用域链和作用域链的作用细节对彻底理解闭包至关重要;
作用域链的本质是一个指向变量对象的指针列表;
当某个函数被调用时就会创建一个执行环境和作用域链;然后使用arguments对象和其他命名参数的值来初始化函数的活动对象;
但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位直至作用域终点的全局作用域。
在函数的执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量;
每个执行环境都有一个表示变量的对象——变量对象,全局环境的变量对象始终存在而局部环境的变量对象只在函数的执行过程中存在;
一般来讲,当函数执行完毕后,局部变量对象就会被销毁,全局变量对象会一直保存。
但是闭包的情况就有所不同了;
因为闭包会携带包含他的外部函数的作用域,所以在内部函数的作用域链上(闭包)就包含着外部函数的活动对象;
这样在外部函数执行完毕后,其活动对象也不会被销毁,因为内部函数的作用域链仍然引用这个活动对象;
但是过度的使用闭包会导致过多的占用内存;
4、闭包与变量:
由于作用域链中保存的是整个变量对象,而不是某个特殊的变量,所以闭包只能取得包含函数中的任何变量的最终值。
下面可一个案例:
function test(){
var arr=new Array();
for(var i=0;i<10;i++){
arr[i]=function(){
return i;
}
}
return arr;
}
这个函数会返回一个函数数组。表面上看似乎每个函数都会返回自己的索引值,但实际上都会返回10;
原因是:i 这个变量是属于外部函数的,他储存在闭包的作用域链上,他的最终值是10;所以每个函数返回的都是10;
如何解决?——让他返回一个匿名函数;
function test(){
var arr=new Array();
for(var i=0;i<10;i++){
arr[i]=(function(num){
return num;
})(i);
}
return arr;
}
这里我们没有将闭包直接赋值给数组,而是将一个匿名立即执行函数赋值给数组,并且参数还是按值传递的;
5、关于this对象:
this对象是基于函数的执行环境决定的,匿名函数的执行环境具有全局性,因此this指向window;
为什么闭包中的this对象不指向外部函数的作用域呢?
因为每个函数在被调用时都会创建两个特殊的变量his和arguments,内部函数在搜素变量时,只会搜索到其活动对象为止,
因此永远不可能访问外部函数中的这两个变量;
不过把外部作用域中的this对象保存在一个闭包可以访问的变量里,就可以让闭包访问了;
var name="zhang";
var Object={
name:"jack",
getName:function(){
var that=this;
return function(){
return that.name;
};
}
};
alert(Object.getName()());//jack
6、模仿块级作用域和私有变量
JavaScript中没有块级作用域和私有变量的概念但可以模仿出来——使用匿名立即执行函数
对比下面两个函数:
function func(){
(function (){
for(var i=0;i<10;i++){
alert(i);
}
})();
alert(i)//访问不了
}
function func(){
for(var i=0;i<10;i++){
alert(i);
}
alert(i)//可以访问
}