javascript:复习匿名函数和闭包

下午睡觉起来之后,看了一下Nicholas C.Zakas的《JavaScript高级程序设计》中的匿名函数这一章,又一次被他的语言,他的思考所折服了。下来把所看到的东西做一次总结。

一、 匿名函数(lambda函数)


1. 下面是普通的函数声明:
方式1:函数声明  

function functionName(arg1, arg2, arg3) {
  
}

方式2:函数表达式:  
var functionName = function(arg1, arg2, arg3) {
  
}
  它们之间的主要区别:函数声明会在代码执行之前被加载作用域中,而后者则是在代码执行到那一行时候才会有定义。另一个重要的区别是函数声明会给函数指定一个名字,而函数表达式则是创建一个匿名函数,然后将这个匿名函数赋给一个变量。也就是第二个例子创建了一个带有三个参数的匿名函数,然后把这个匿名函数赋给了变量functionName;但是,并没有给这个匿名函数指定名字。
2. 匿名函数也可以像下面这一个样子来写:
Function(arg0, arg1, arg2) {
}
而这一种写法可以有两个用途:
1)、将函数作为参数传入另一个函数。

2)、 从一个函数中返回另一个函数。


二、 递归函数要注意的一些问题。


下面是一个递归函数:  
fuction factorial(num) {
  if(num < = 1) {
        return 1;
  }else {
         Return num * factorial(num - 1);
  }
}


虽然这个函数没有什么问题,但是下面的代码可能会导致它的出错:
   var aotherFactorial = factorial;
  factorial = null;
  alert(anotherFactorial (4));

  在这里先把factorial()函数保存在变量anotherFactorial中,然后将factorial变量设置为null,结果指向原始函数的引用只剩下一个(anotherFactorial)。但是在接下来调用anotherFactorial()时,由于必须执行factorial(),而factorial已经不再是一个函数,这样就会导致有问题。
  在这种情况下,使用arguments.callee可以解决这个问题。它是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用。   
fuction factorial(num) {
  if(num < = 1) {
       return 1;
  }else {
       Return num * arguments.callee(num - 1);
  }
}
var aotherFactorial = factorial;
factorial = null;
alert(anotherFactorial (4));

即总的来说:在编写递归函数时,使用arguments.callee总比使用函数名更保险。


三、 闭包


含义:指有权访问另一个函数作用域中的变量的函数。它使用的是作用域链的配置机制。即闭包只能取得包含函数中任何变量的最后一个值。所以会的执行的时候出现一些问题。(下面的例子和我之前遇到的问题很相似)

写入一个html代码:

<!DOCTYPE HTML> 
<html> 
<head> 
<meta charset="utf-8" /> 
<title>闭包演示</title> 
<style type="text/css"> 
p {background:gold;} 
</style> 
<script type="text/javascript"> 
function init() { 
     var pAry = document.getElementsByTagName("p"); 
     for( var i=0; i<pAry.length; i++ ) { 
           pAry[i].onclick = function() { 
                alert(i); 
           };
     } 
} 
</script> 
</head> 
<body οnlοad="init();"> 
<p>产品 0</p> 
<p>产品 1</p> 
<p>产品 2</p> 
<p>产品 3</p> 
<p>产品 4</p> 
</body> 
</html> 
本来以为点击产品0,产品1,产品2能分别出现0,1,2,3,但其实最后每一个都出现了5这个数字。这是我遇到的最头疼的问题之一。在这里可以这样去解释,for循环其实很快就结束了,而onclick事件为异步执行,也就是它们都在等待被点击的那一时刻,但是在点击之前i指向的变量已经为5了,所以之后 每一次响应点击事件的时候取到的都是i指向的变量“5”。我的理解是在这里它们取到的不是i的值,而是i指向的的变量的值。

下面是两个比较好的处理方法.

方法1(这个方法我有使用过,很有效):

function init1() { 
      var pAry = document.getElementsByTagName("p"); 
      for( var i=0; i<pAry.length; i++ ) { 
            pAry[i].i = i; 
            pAry[i].onclick = function() { 
                 alert(this.i); 
            };
      } 
} 

方法2(在onclick中传入一个变量,使它以值的形式出现在匿名函数中,其实这种方法我自己并不是理解的很好,希望之后能看到对它的更好的解释):
function init1() { 
     var pAry = document.getElementsByTagName("p"); 
     for( var i=0; i<pAry.length; i++ ) { 
         pAry[i].i = i; 
         pAry[i].onclick = function(num) { 
              return function(){
                   alert(num); 
              }
         }(i); 
     } 

四、 使用this对象出现的问题 (原因:匿名函数的执行环境具有全局性)

例子1:

var name = "This window";
var object = {
	name:"My Object";
	getNameFunc : function() {
	     return function() {
		 return this.name;
	 };
      	}
};
alert(object.getNameFunc()());//"The window";

在这里得到的并不是一开始感觉到的“My Object”,这里就很好地解释了“ 匿名函数的执行环境具有全局性”

下面改变做法:把外部作用域中的this对象保存在一个闭包能够访问到的变量里。

var name = "This window";
var object = {
	name:"My Object";
	getNameFunc : function() {
	var that = this;
	return function() {
		return that.name;//My Object
	};
      	}
};

在这里,问题就解决了。

五、 使用闭包建立私有作用域,模仿块级作用域,同时减少闭包占用的内存问题(使外部函数没有指向匿名函数的引用,只要函数执行完毕就可以立即销毁其作用域链了)。


第一个例子:

function outputNumbers(count) {
	for(var i = 0;i < count; i++) {
		alert(i);
	}
	alert(i);//count
}//javascript中没有类似java,c#等中的块级作用域。i的作用域为outputNumbers这个函数中

第二个例子:

function outputNumbers(count) {
	for(var i = 0;i < count; i++) {
		alert(i);
	}
	var i;
	alert(i);//count
}//在javascript中,它从来不会告诉你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(不过,它会执行后续声明中的变量初始化);

第三个例子:
function outputNumbers(count) {
	for(var i = 0;i < count; i++) {
		alert(i);
	}
	var i = 2;
	alert(i);//2
}

第四个例子:
function outputNumbers(count) {
	(function() {
		for(var i = 0;i < count; i++) {
		alert(i);
		}
	})();
	alert(i);//导致一个错误;
}//在for循环外部插入一个私有作用域,在匿名函数中定义的任何变量,都会在执行结束时销毁。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值