第7章 函数表达式

定义函数的两种方式:函数声明、函数表达式

函数声明:function name(arg1,arg2){          //  “function”是关键字   “name”  是函数名字

                         // 函数体

                  }

                 函数声明的一个重要特征是  函数声明提升   意思是在读取代码前会读取函数声明。这意味着可以把函数声明放在调用它 的代码后面。栗子:

                 sayhi();

                 function sayhi(){ alert(" hi ") }

函数表达式:var  funcName = function(arg1,arg2){

                            // 函数体

                     }

                    解释:创建了一个 匿名函数 (因为function关键字后面没有标识符)赋值给了变量 funcName。函数表达式在使用前必须赋值。否则会导致错误。栗子:

                    sayhi();    //  错误,函数还不存在

                    var sayhi = function(){ alert("hi") }

 理解函数提升的关键,在于理解函数声明和函数表达式之间的区别。栗子:

// 不要这样做

if(condition){ 
     function sayHi(){ 
           alert("Hi!"); 
     } 
} else { 
       function sayHi(){ 
             alert("Yo!"); 
        } 

//可以这样做
var sayHi; 
if(condition){ 
            sayHi = function(){ 
                 alert("Hi!"); 
 }; 
} else { 
            sayHi = function(){ 
                 alert("Yo!"); 
 }; 

7.1递归

通过名字调用自身。

会导致错误的栗子:

function fac(num){
   if(num<=1){
      return 1
   }else{
      return num * fac(num-1)
    }
}

var anotherFactorial = factorial; 
factorial = null; 
alert(anotherFactorial(4)); //出错!

正确的用法1:arguments.callee  (非严格模式)

function factorial(num){ 
    if (num <= 1){ 
            return 1; 
     } else { 
     return num * arguments.callee(num-1); 
 } 

正确的用法2:命名函数表达式  (严格模式)

var factorial = (function f(num){ 
      if (num <= 1){ 
           return 1; 
      } else { 
            return num * f(num-1); 
      } 
}); 

7.2闭包

有权访问另一个函数作用域中变量的函数。创建闭包的方式:大函数套小函数。

function c(name){
   return function(o1,o2){
      var val1 = o1[name];
      var val2 = o2[name];

      if(val1<val2){ return -1;}
      else if(val1>val2){
        return 1
      }else{
        return 0
      }
   }
}

解释:突出的那两行代码是一个匿名函数中的代码,他们访问了外部函数中的变量name。即使这个匿名函数被返回了,而且在其他地方调用,但它仍然可以访问变量name。值所以能访问这个变量,是因为这个匿名函数的作用域链中包含c()的作用域。

在函数中访问一个变量时,会从作用域中搜索相应名字的变量。当函数执行完毕后,局部活动对象会被销毁,内存中仅保存全局作用域。但是闭包情况不同。

在另一个函数内部定义的函数,将包含外部函数的活动对象添加到它的作用域中。因此在c()函数内部定义的匿名函数的作用域链中,实际上会包含外部函数c()的活动对象。c()函数在执行完毕后,其活动对象也不会销毁,因为匿名函数的作用域仍然在引用这个对象。意思是,当c() 函数返回后,其执行环境的作用域会被销毁,但它的活动对象仍然会留在内存中。直到匿名函数被销毁后,c()的活动对象才会销毁。栗子:

var creat = hreat(" name ");  // 创建函数

var result = creat( { name: "ni"} , { name: "gery"} );  // 调用函数

creat = null;

解释:首先创建都变量保存在creat中。通过将creat设置为null解除该函数的引用,就等于通知垃圾回收将其清除。

7.2.1闭包与变量

作用域链的这种配置机制有个副作用,即闭包只能取得包含函数中任何变量的最后一个值。但其实闭包所保存的是整个变量对象,而不是某个特殊的变量。栗子:

function c(){
   var r = new Array();
   for(var i=0;i<10;i++) {
            r[i] = function(){return i}
   }
   return r
}

解释:表面上这个函数会返回一个函数数组。每个函数都应该返回自己的索引值,即位置0的函数返回0,位置1的函数返回1,.....。但实际上,每个函数都返回10,因为每个函数的作用域中都保存着c() 函数的活动对象,所以它们引用的都是同一个变量i。但是我们可以通过创建另一个匿名函数强制让闭包的行为符合预期。栗子:

function c(){
   var r = new Array();
   for(var i=0;i<10;i++){
      r[i] = function(n){
         return function(){
            return n
         }
      }(i);
   }
   return r;
}
返回  1到9不同的值

7.2.2关于this对象

this对象是在运行时基于环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。匿名函数的执行环境具有全局性,其this对象指向window。

var name = "the window";

var object = {

   name:"my object",

   getName:function(){

           return function(){

                   return this.name

           }
   }

}

alert( object.getName() )   //  "the window"

解释:函数被调用时会获取两个变量,this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,永远不可能访问到外部函数中的这两个变量。不过,把外部作用域中的this对象保存在闭包能够访问到的变量里,就可以让闭包访问该变量了。栗子:

var name = "the window";

var object = {

   name:"my object",

   getName:function(){

         var that = this;

         return function () {

              return that.name

         }

   }

}

alert( object.getName() )    //  "my object"

解释:在定义匿名函数之前,把this对象赋值给了that的变量。在定义了闭包之后,闭包也可以访问这个变量,因为它是我们在包含函数中特意声明的一个变量。

7.2.3内存泄漏

如果闭包作用域中保存着一个html元素,那么该元素将无法被销毁。栗子:

function assign() {

     var el = document.getElementById( "someEl" );

     el.onclick = function() {

          alert(el.id)

     }

}

这个闭包又创建了一个循环引用。由于匿名函数保存了一个对assign()的活动对象的引用,因此就无法减少对el的引用数。因此el所占的内存就永远不会被回收。不过可以通过以下方式解决。栗子:

funtion assign() {

       var el = document.getElementById( "someEl" ); 

       var id = el.id;      

       el.onclick = function() {

             alert(id)

        };

       el =  null;

}

解释:通过把el.id的一个副本保存在一个变量中,并且在闭包中引用该变量消除了循环引用。但这样还不能解决内存泄漏的问题。必须要记住:闭包会引用包含整个活动对象,而其中包含着el。即使闭包不直接引用el,包含函数的活动对象中也仍然会保存一个引用。因此必须把el变量设置为null。这样就能够解除对DOM对象的引用的,顺利减少其引用数,确保正常回收其占用的内存。

7.3模仿块级作用域

(function () {   // 私有作用域

      // 这里是块级作用域

}) () 

这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。在一个由很多开发人员共同参与的大应用程序中,过多的全局变量和函数很容易导致命名冲突。而通过创建私有作用域,每个开发人员有自己的变量也不会搞乱全局作用域。栗子:

(function(){

    var now = new Date();

    if(now.getMonth() == 0 && now.getDate() == 1){

         alert( "happy new year!" ) 

    }

})()

解释:变量now现在是匿名函数中的局部变量,而我们不必在全局作用域中创建它。这种做法可以减少闭包占用的内存问题,因为么有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁作用域链了。

7.4私有变量

任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外面访问这些变量。私有变量包括函数的参数、局部变量和在函数 内部定义的其他函数。栗子:

function add(num1,num2){

   var sum = num1+num2;

   return sum;

}

解释:这里有3个私有变量 num1,num2和sum。函数内部可以访问它们但函数外部不可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值