首先,一个小小的题目:将10以内的数字进行输出。相信大家看到这个题目一定会感到非常熟悉,这不就是一层简单的for循环吗? 不就是这样一段demo:
function test1(){
var count = 0;
for(var i =0; i < 10;i++){
document.write(i+" ");
}
}
test1();
运行结果就是:0 1 2 3 4 5 6 7 8 9
相信上面的题目只要学过编程的人都会轻松的写出来。那么题目要求改变一下:要求输出一个数组内的10个函数的索引值。函数索引值?直接把数组角标输出不就行了,比上面多两行代码而已,代码如下:
function test(){
var arr = [];
for(var i = 0;i < 10;i ++){
arr[i] = function(){
document.write(i + " ");
}
}
return arr;
}
var myArr = test();
for(var j = 0;j < 10;j++){
myArr[j]();
}
从上面的代码可以看见:使用for循环给数组内储存十个函数,并调用函数再次使用for循环输出。看起来简直天衣无缝。但是....运行结果却是这样的:
当我看到这个运行结果我也很纳闷。首先,输出10个数。这是毋庸置疑的。但是10个数一样就很怪异了,更让人费解的是为什么输出了10,输出10个9我能理解(第一段demo输出0-9)。
闭包
闭包包含自由(未绑定到特定对象)变量,这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。
function a(){
function b(){ //内部函数
var bbb = 234;
console.log(aaa);
}
var aaa = 123;
}
var glob = 100;
var demo = a();
demo();
从上面的demo可以看到,先进行定义a函数,a函数内部有一b函数,b函数用控制台进行aaa的输出。最后用demo将a进行调用,程序结束。 相信有一半的人会认为是这样,运行结果是:
不但没有结果,还出现了报错。请再把闭包的概念看一遍:变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。作用域不同,其实就是局部变量不能在全局使用。只需一行即可完成demo。
function a(){
function b(){ //内部函数
var bbb = 234;
console.log(aaa);
}
var aaa = 123;
return b; //return 返回外部(保存在外部) 闭包
}
var glob = 100;
var demo = a();
demo();
运行结果:
现在我们返回第二个题目,输出函数索引。可能有人会想直接加一个return arr,把数组返回到外部就OK了,但是仔细看demo,已经存在返回的数组arr。
为什么会是10?
当i加到9的时候,9<10,再进行i++;
为什么会有10个10?
函数在外部for循环进行调用,那么已经在函数内部进行了预编译,此时的i值已经全部变成了10,外部再一个一个的进行调用,所以输出了10个10。
如何改正demo使其正确?
引入概念:立即执行函数(IIFE)。
Javascript和其他编程语言相比比较随意,所以Javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解。其中( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法。
首先得知道这三个名词:函数声明,函数表达式,匿名函数。
函数声明: function fnName() {...} ;
使用 function 关键字声明一个函数,再执行一个函数名,叫函数声明。
函数表达式: var fnName = function() { ... } ;
使用 function 关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。
匿名函数: function() { ... } ;
使用 function 关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。
以下是我今天听课所学习到的关于立即执行函数的知识:
//立即执行函数
//针对初始化功能的函数 执行完就被释放
(function (){
var a = 123;
var b = 234;
console.log(a+b);
}())
var num = (function(a,b){
var c = a+b*4;
return c;
}(234,564));
console.log(num);
//(function(){}()); //建议第一种
//(function(){})();
//只有表达式才能被执行符号执行
function test(){
var a = 213;
}
test();
var test = function(){
console.log('a');
}
//能被执行符号执行的表达式变成立即执行函数
var test = function(){
console.log('a');
}();
+ function test(){ //+ - ! (*和/不行 + - 是正负不是加减)
console.log('a');
}();
var num = function test(a , b , c , d){
console.log(a+b+c+d);
}(1 , 2 , 3 , 4);
通过闭包和立即执行函数,这是我们就可以改正我们之前的demo了。
function test(){
var arr = [];
for(var i = 0;i < 10;i ++){
(function (j){
arr[j] = function(){
console.log(j + " ");
}
}(i));
}
return arr;
}
var myArr = test();
for(var j = 0;j < 10;j++){
myArr[j]();
}
运行结果:
以上若有不足之处,还请指出。