下午睡觉起来之后,看了一下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循环外部插入一个私有作用域,在匿名函数中定义的任何变量,都会在执行结束时销毁。