第七章 函数表达式
定义函数表达式的方式有两种:一种是函数声明,另一种就是函数表达式。
注意:函数是一个对象,函数名是一个指向函数对象的指针,是一个变量。
函数声明的语法:
function functionName(arg1,arg2,arg3){}
关于函数声明,一个重要特征就是函数声明提升,意思是在执行代码之前会先读取函数声明。意味着可以先调用,后声明函数。
第二种创建函数的方式就是使用函数表达式。
var functionName=function(arg1,arg2,arg3){};
看上去好像是常规的变量赋值语句,即创建一个函数并将它赋给变量functionName(把函数当成值来使用)。这种情况下创建的函数叫匿名函数,因为function关键字后面没有标识符。
函数表达式与其他表达式一样,使用前必须赋值。
1、递归
递归是在一个函数通过名字调用自身的情况下构成的。如:
function factorial(num){
if(num<=1){
return 1;
}
else{
return num*factorial(num-1);
}
}
函数是一个对象,函数名是一个指向函数对象的指针,是一个变量。
var anotherFac=factorial;
factorial=null;
alert(anotherFac(3)); //错误!
原因在于anotherFac函数现在是指向function函数的一个指针,而函数内factorial已经被置为空,不再是函数,无法调用。可以使用argument.callee解决这个问题。
将factorial改为argument.callee指向现在调用这个函数的指针,来实现递归。
还有一种方法是使用函数表达式。
var factorial=function f(num){
if(num<=1){
return 1;
}
else{
return num*f(num-1);
}
2、闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
函数周期:每一个函数分为两种储存方式 一个是调用时 一个是没调用时 (活着的 , 死着的) 而调用时会创建一个活动对象(函数作用域对象)AO 这个对象就是保存着所有的局部变量 当函数执行结束 这个对象就会随着释放 里面的变量也就会随着消失 所以函数重复使用的时候 里面的变量每次都是初始值
闭包就是 让一个函数的活动对象不被释放 这样的话 函数内部所有的变量将不会消失 可以继续使用。
实现的方法就是 让这个函数(外层函数)去返回一个新的函数(内层函数,内层函数的保存形式是多种的 可以保存在数组中 但是重点是 内层函数引用外层函数的活动对象) 内层函数会自动引用外层函数的活动对象 (利用作用域链) 但是内层函数 调用之后会释放掉 从而外层函数也释放了 解决的方法就是 用一个变量去引用 内层函数 这样就形成了闭包
如
function fun(){ //外层函数
var a = 10; // 外层函数活动对象中的 变量
return function(){ // 内层函数 对外层函数活动对象的引用
console.log(a++) // 操作外层函数的局部变量
}
}
var getName = fun(); // 引用外层函数的返回值 注意 函数的return 是将任意一个东西返回出来
// 上面这一步是最重要的一步 决定了闭包是否形成 和 想访问 受保护的变量 只能通过getName
getName();//10
getName();//11
getName();//12
当某个函数被调用时,会创建一个执行环境及相应的作用域链。然后,使用arguments和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位……直至作为作用域链终点的全局执行环境。
在函数执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量。
function compare(value1,value2){
if(value1<value2){}
else if……
}
var result=compare(5,10);
以上代码先定义了compare函数,然后又在全局作用域中调用了它。当调用compare()时,会创建一个包含arguments(此例中就是[5,10])、value1、value2的活动对象。全局执行环境的变量对象(包含result、compare)在compare()执行环境的作用域链中则处于第二位。
后台的每个执行环境都有一个表示变量的对象——变量对象。全局环境的变量对象始终存在,而像compare()函数这样的局部环境的变量对象,则只在函数执行的过程中存在。在创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。当调用compare()函数时,会为函数创建一个执行环境,然后通过复制函数[[Scope]]属性中的对象构建起执行环境的作用域链。此后,又有一个活动对象(在此作为变量对象使用)被创建并被推入执行环境作用域链的前端。对于这个例子中compare()函数的执行环境而言,其作用域链中包含两个变量对象:本地活动对象和全局变量对象。显然,作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。一般来讲,函数执行完毕后,局部活动对象就会被销毁,内存中只保存全局作用域。但闭包的情况又有所不同。
在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。。。。。
还有this。。。这节好难,回头再看吧- -
3、模仿块级作用域
js没有块级作用域的概念,意味着在块语句中定义的变量实际上是在包含函数中而非语句中创建的。如循环
function kkk(k){
for(var i=0;i<k;i++){
alert(i);
}
alert(i); // 依然有效。只要定义了i就可以在内部访问它。
}
用匿名函数可以模仿块级作用域(私有作用域)并避免这个问题。
(function(){
//代码
})();
函数声明后不可以跟圆括号但函数表达式可以。函数声明转化为函数表达式只需要加圆括号(像上面一样)。
这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。防止其他开发人员与自己创建的变量、函数发生冲突。
4、私有变量