第七章 函数表达式
1. 定义函数的两种方法 :
//函数声明法
function sayHi(){
alert("Hi!");
}
//关于函数声明法,他的一个重要特征就是函数声明提升,即在执行代码之前会先读取函数声明,意味着我们可以把函数声明放在调用它语句的后面
//函数表达式
var sayHi = function(){
alert("Hi!");
}
//关于两者的区别
//这样做会报错
if(condition){
function sayHi(){
alert("Hi"!);
}
}
else{
function sayHi(){
alert("Yo!");
}
}
//但这样就不会
if(condition){
sayHi = function(){
alert("Hi"!);
}
}
else{
sayHi = function(){
alert("Yo!");
}
}
2. 递归 :
递归函数是在一个函数通过名字调用自身的情况下构成的。
function factorial(num){
if(num <= 1){
return num;
}
else{
return num * factorial(num - 1);
}
}
//这是一个经典的递归阶乘函数,虽然这个函数表面看起来没什么问题,但下面的代码却可能导致塔出错
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出错
//原因是调用anotherFactorial()时,由于必须执行factorial(),所以就会导致错误
//解决方法
function factorial(num){
if(num <= 1){
return num;
}
else{
return num * arguments.callee(num - 1);
}
}
//但在严格模式下不可以通过脚本来访问arguments.callee,不过可以通过命名函数表达式来达到相同的结果
var factorial(num){
if(num <= 1)
{
return num;
}
else
{
return num * f(num - 1);
}
}
//以上代码创建了一个名为f的命名函数表达式,然后将它赋值给变量factorial,即使把函数赋值给了另一个函数,函数的名字f仍然有效,所有递归调用照样完成,这种方式在严格或非严格模式下都可以行得通
3. 闭包 :
-
闭包 :
指的是有权访问另一个函数作用域中变量的函数 -
闭包的用途 :
可以读取函数内部的变量并对其作出修改。
可以让这些变量的值始终保存在内存中。
//读取变量的值
function f1(){
var n = 10;
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result(); //10
//对变量的值作出修改
function f1(){
var n = 10;
function f2(){
alert(n + 1);
}
return f2;
}
var result = f1();
result(); //11
//或者这样对函数作出修改
function f1(){
var n = 10;
function add(n){
n += 1;
}
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result(); //10
add();
result(); //11
//在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是10,第二次的值是11。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
//为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
在闭包模式下,this 对象有时候也可能会出现问题。
var name = "the window";
var object = {
name: "the object",
getNameFun: function(){
return function (){
return this.name;
}
}
};
alert(object.getNameFun()()); //the window
如果想访问对象内部的属性,只需要这样就可以了。
var name = "the window";
var object = {
name: "the object",
getNameFun: function(){
var that = this;
return function (){
return that.name;
}
}
};
alert(object.getNameFun()()); //the object
4. 模仿块级作用域 :
如前所述,JavaScript中没有块级作用域的概念,这意味着在 块语句 中定义的变量,实际上是在函数内部创建的(并不是 块语句 内部)。
function numbers(count){
for(var i=0;i < 10;i++)
{
alert(i);
}
alert(i); //计数,i变量仍然可以被访问
}
//上述即使你再块级语句下面再声明一次变量i也无济于事
匿名函数可以用来模仿块级作用域来解决这个问题。
//通用语法如下所示
(function (){
//这里是块级作用域
})(); //后面的括号表示声明函数后立即执行匿名函数
所以上述例子引用块级作用域后应该为…
function numbers(count){
(function (){
for(var i=0;i < 10;i++)
{
alert(i);
}
})();
alert(i); //导致错误
}