第 7章 函数表达式
一.声明函数
1.使用关键字 function
Function functionname(参数1,参数2){}
此方法的特征是:函数声明提升,意思就是调用可以放在声明的前面
2.使用函数表达式
Var functionname=function(参数1,参数2){}
此种方法更像是常量之间的赋值语句,即创建一个函数并将它赋值给一个变量,此种创建的 函数称为匿名函数,因为function关键字后面没有标识符。
区别:
函数声明:
If(condition){
Function sayHi(){
Alert(“hi”)
}
}
Else{
Function sayHi(){
Alert(“Yo”)
}
}
词语法是无效的ECMAscript语法,
但是可以函数表达式:
Var sayHi;
If(condition){
sayHi=function(){
Alert(“hi”)
}
}
Else{
sayHi=function(){
Alert(“Yo”)
}
}
二. 递归
函数递归就是一个函数通过名字调用自身的情况下构成的,
因此可以用的,arguments.callee属性
1.函数内部的属性
a) Arguments:他是一个类数组对象,包含着传入函数中的所有参数,它还有一个名为 callee的属性,该属性是一个指针,指向拥有Arguments对象的函数(当前运行的函数)
类如计算阶乘:
Function factorial(num){
If(num<1){
Return 1
}else{
Return num*factorial.callee(num-1)
}
b) this 引用的是函数据以执行的环境对象。
c) caller这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前 函数,它的值为null.
三. 闭包
闭包就是有权访问另一个函数作用域中的变量的函数。创建一个闭包最常见的方法就是一个函数内部创建另一个函数(然后把被包含的函数作为变量返回)
function f1(){
n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); 因为发f1返回的是f2函数,所以在result()调用时,类似调用f2函数
1.闭包与变量
由于作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值.别忘了闭包所保存的是整个变量对象,而不是某个特殊的对象.
如:function createFunctions(){
Var result=new Array();
For(var i=0;i<10;i++){
Result[i]=function(){
Return i;
}
}
Return result}
以上代码看似返回的是每个函数的索引,但是实际每个函数返回的都是10;要想实现每个函数返回的都是10,则加另一个匿名函数
function createFunctions(){
Var result=new Array();
For(var i=0;i<10;i++){
Result[i]=function(num){
Return function(){
Return num;
};
}(i);
}
Return result}
2.关于this对象
因为匿名函数的执行环境具有安全性,因此其this对象通常指向window.
如:
Var name=”the window”
Var object={
Name:”my object”;
getNameFunc:function(){
Return function(){
Return this.name
}
}
}
Alert(object.getNameFunc()()); //the window
可以把外部作用域中的this对象保存在一个闭包能够访问到的变量里就可以让闭包访问该对象了;
Var name=”the window”
Var object={
Name:”my object”;
getNameFunc:function(){
Var that=this
Return function(){
Return that.name
}
}
}
Alert(object.getNameFunc()()); //my obejct
3.内存泄露
如果闭包作用域中保存一个html元素,那么就意味着该元素无法被销毁;
Function assignhandler(){
Var element=documentById(“someElement”);
Element.onclick=function(){
Alert(element.id)
};
}
以上代码创建一个作为element元素事件处理程序的闭包,而这个闭包又创建了一个循环引用,由于匿名函数保存一个对assignhandler()的活动对象的引用。因此就导致无法减少对element的引用数,因此他占用内存就永远不会被收回。
改进:
Function assignhandler(){
Var element=documentById(“someElement”);
Var id=element.id;
Element.onclick=function(){
Alert(id)
};
Element=null;
}
通过把 elemenyt.id的一个副本保存在一个变量里,而且在闭包中引用该变量,消除了循环引用。闭包会引用包含函数的整个活动对象,其中包含着element,即使闭包不直接引用element,包含函数的活动对象中也仍然会保存一个引用。因此有必要把element设为Null. (包含函数就是父函数)
4.模仿块级作用域
JavaScript中没有块级作用域的概念,意味着在块语句定义的变量,实际上是在包含函数中而非语句中创建的,
把函数声明转换为函数表达式,只要像下面:
(function (){
/这里是块级代码作用域
])()
像上面一样如需零时的需要一些变量就可以使用私有作用域
Function outputNumbers(count){
(function () {
For(var i=0;i<count;i++){
Alert(i)
}
})();
Alert(i) //导致一个错误
}以上代码我们在for循环外部插入了一个私有作用域,在匿名函数中定义的任何变量,都会在执行结束时被销毁,因此,变量I只能在循环中使用。
5.私有变量
任何在函数中定义的变量都是私有变量,因为不能在函数外部访问这些变量
1.私有变量包括:函数的参数 局部变量 函数内部定义的其他函数,
2.通过闭包可以访问私有变量,利用这一点就可以创建用于访问私有变量的公有方法。我们把有权访问私有变量和私有函数的公有方法称为特权方法。
Function myObject(){
//私有变量和私有函数
Var privateVariable=10;
Function privateFunction(){
Return fasle;
}
//特权方法
this.publicMethod=function(){
privateVariable++;
Return privateFunction();
}
}
对与以上而言,privateVariable和privateFunction()只能通过特权方法来访问。在创建myObject实例后,只有这一个方法才可以访问以上私有变量和私有函数