《JavaScript高级程序设计》之笔记五

第七章 函数表达式

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);    //导致错误
}

你可能感兴趣的:(《JavaScript高级程序设计》之笔记五)